annotate.c 25.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
#include <newt.h>
14

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

27 28 29 30 31 32 33 34 35 36
static struct annotate_browser_opt {
	bool hide_src_code,
	     use_offset,
	     jump_arrows,
	     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 132 133 134
	else if (dl->offset == -1) {
		printed = scnprintf(bf, sizeof(bf), "%*s  ",
				    ab->addr_width, " ");
		slsmg_write_nstring(bf, printed);
135
		slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
136
	} else {
137
		u64 addr = dl->offset;
138
		int color = -1;
139

140
		if (!annotate_browser__opts.use_offset)
141 142
			addr += ab->start;

143
		if (!annotate_browser__opts.use_offset) {
144
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
145
		} else {
146
			if (bdl->jump_sources) {
147
				if (annotate_browser__opts.show_nr_jumps) {
148 149 150 151 152 153 154
					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);
155
					ui_browser__set_color(browser, prev);
156 157
				}

158
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
159
						    ab->target_width, addr);
160
			} else {
161 162
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
163 164
			}
		}
165

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

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

193
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
194
		slsmg_write_nstring(bf, width - pcnt_width - 3 - printed);
195
	}
196

197
	if (current_entry)
198
		ab->selection = dl;
199 200
}

201 202 203 204 205 206 207 208 209 210
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;
}

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

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

225
	if (!disasm_line__is_valid_jump(cursor, sym))
226
		return;
227

228 229 230
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
231

232 233
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
234

235
	if (annotate_browser__opts.hide_src_code) {
236
		from = bcursor->idx_asm;
237 238
		to = btarget->idx_asm;
	} else {
239
		from = (u64)bcursor->idx;
240 241 242
		to = (u64)btarget->idx;
	}

243 244
	pcnt_width *= ab->nr_events;

245
	ui_browser__set_color(browser, HE_COLORSET_CODE);
246 247
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
248 249 250 251
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
252
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
253
	int ret = ui_browser__list_head_refresh(browser);
254 255 256
	int pcnt_width;

	pcnt_width = 7 * ab->nr_events;
257

258
	if (annotate_browser__opts.jump_arrows)
259
		annotate_browser__draw_current_jump(browser);
260

261
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
262
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
263 264 265
	return ret;
}

266 267 268 269 270 271 272 273 274 275 276 277 278 279 280
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)
281
{
282
	struct rb_node **p = &root->rb_node;
283
	struct rb_node *parent = NULL;
284
	struct browser_disasm_line *l;
285 286 287

	while (*p != NULL) {
		parent = *p;
288
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
289 290

		if (disasm__cmp(bdl, l, nr_events))
291 292 293 294
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
295 296
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
297 298
}

299
static void annotate_browser__set_top(struct annotate_browser *browser,
300
				      struct disasm_line *pos, u32 idx)
301 302 303
{
	unsigned back;

304 305 306
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
307

308
	while (browser->b.top_idx != 0 && back != 0) {
309
		pos = list_entry(pos->node.prev, struct disasm_line, node);
310

311
		if (disasm_line__filter(&browser->b, &pos->node))
312 313
			continue;

314
		--browser->b.top_idx;
315 316 317
		--back;
	}

318 319
	browser->b.top = pos;
	browser->b.navkeypressed = true;
320 321 322 323 324
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
325
	struct browser_disasm_line *bpos;
326
	struct disasm_line *pos;
327
	u32 idx;
328

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

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

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

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

364 365 366 367 368 369 370 371 372 373 374 375
		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) {
376
			RB_CLEAR_NODE(&bpos->rb_node);
377 378
			continue;
		}
379 380
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
381 382 383 384 385 386
	}
	pthread_mutex_unlock(&notes->lock);

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

387 388
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
389
	struct disasm_line *dl;
390
	struct browser_disasm_line *bdl;
391 392 393
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
394
	dl = list_entry(browser->b.top, struct disasm_line, node);
395
	bdl = disasm_line__browser(dl);
396

397
	if (annotate_browser__opts.hide_src_code) {
398 399
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
400 401

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

413 414
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
415 416

		browser->b.nr_entries = browser->nr_asm_entries;
417
		annotate_browser__opts.hide_src_code = true;
418
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
419 420
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
421 422 423 424 425
	}

	return true;
}

426 427 428 429 430 431
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;
}

432 433
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
434
				    struct hist_browser_timer *hbt)
435 436
{
	struct map_symbol *ms = browser->b.priv;
437
	struct disasm_line *dl = browser->selection;
438 439 440 441 442
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

443
	if (!ins__is_call(dl->ins))
444 445
		return false;

446
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463
	target = map__find_symbol(ms->map, ip, NULL);
	if (target == NULL) {
		ui_helpline__puts("The called function was not found.");
		return true;
	}

	notes = symbol__annotation(target);
	pthread_mutex_lock(&notes->lock);

	if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
			    target->name);
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
464
	symbol__tui_annotate(target, ms->map, evsel, hbt);
465 466 467 468
	ui_browser__show_title(&browser->b, sym->name);
	return true;
}

469 470 471
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
472 473 474 475
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
476
	struct disasm_line *pos;
477 478 479 480 481

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
482
		if (!disasm_line__filter(&browser->b, &pos->node))
483 484 485 486 487 488 489 490
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
491
	struct disasm_line *dl = browser->selection;
492
	s64 idx;
493

494
	if (!ins__is_jump(dl->ins))
495 496
		return false;

497
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
498
	if (dl == NULL) {
499 500 501 502
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

503
	annotate_browser__set_top(browser, dl, idx);
504 505 506 507
	
	return true;
}

508 509 510
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
511 512 513 514
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
515
	struct disasm_line *pos = browser->selection;
516 517 518

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
519
		if (disasm_line__filter(&browser->b, &pos->node))
520 521 522 523 524 525 526 527 528 529 530 531 532
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
533
	struct disasm_line *dl;
534 535
	s64 idx;

536 537
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
538 539 540 541
		ui_helpline__puts("String not found!");
		return false;
	}

542
	annotate_browser__set_top(browser, dl, idx);
543 544 545 546
	browser->searching_backwards = false;
	return true;
}

547 548 549
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
550 551 552 553
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
554
	struct disasm_line *pos = browser->selection;
555 556 557

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
558
		if (disasm_line__filter(&browser->b, &pos->node))
559 560 561 562 563 564 565 566 567 568 569 570 571
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
572
	struct disasm_line *dl;
573 574
	s64 idx;

575 576
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
577 578 579 580
		ui_helpline__puts("String not found!");
		return false;
	}

581
	annotate_browser__set_top(browser, dl, idx);
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 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
	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);
}

634 635 636 637 638 639 640 641 642 643 644 645 646
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;
}

647 648
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
649
				 struct hist_browser_timer *hbt)
650 651
{
	struct rb_node *nd = NULL;
652
	struct map_symbol *ms = browser->b.priv;
653
	struct symbol *sym = ms->sym;
654
	const char *help = "Press 'h' for help on key bindings";
655
	int delay_secs = hbt ? hbt->refresh : 0;
656
	int key;
657

658
	if (ui_browser__show(&browser->b, sym->name, help) < 0)
659
		return -1;
660

661
	annotate_browser__calc_percent(browser, evsel);
662

663 664 665
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
666
	}
667

668
	nd = browser->curr_hot;
669

670
	while (1) {
671
		key = ui_browser__run(&browser->b, delay_secs);
672

673
		if (delay_secs != 0) {
674
			annotate_browser__calc_percent(browser, evsel);
675 676 677 678 679 680 681 682 683
			/*
			 * 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;
		}

684
		switch (key) {
685
		case K_TIMER:
686 687
			if (hbt)
				hbt->timer(hbt->arg);
688 689

			if (delay_secs != 0)
690
				symbol__annotate_decay_histogram(sym, evsel->idx);
691
			continue;
692
		case K_TAB:
693 694 695
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
696
					nd = rb_last(&browser->entries);
697
			} else
698
				nd = browser->curr_hot;
699
			break;
700
		case K_UNTAB:
701 702 703
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
704
					nd = rb_first(&browser->entries);
705
			else
706
				nd = browser->curr_hot;
707
			break;
708
		case K_F1:
709
		case 'h':
710
			ui_browser__help_window(&browser->b,
711 712 713 714 715
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
		"->            Go to target\n"
		"<-            Exit\n"
716
		"H             Cycle thru hottest instructions\n"
717 718 719 720 721 722
		"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"
723
		"r             Run available scripts\n"
724 725
		"?             Search previous string\n");
			continue;
726 727 728 729 730
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
731
		case 'H':
732
			nd = browser->curr_hot;
733
			break;
734
		case 's':
735
			if (annotate_browser__toggle_source(browser))
736 737
				ui_helpline__puts(help);
			continue;
738
		case 'o':
739
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
740
			annotate_browser__update_addr_width(browser);
741
			continue;
742
		case 'j':
743
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
744
			continue;
745
		case 'J':
746
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
747
			annotate_browser__update_addr_width(browser);
748
			continue;
749
		case '/':
750
			if (annotate_browser__search(browser, delay_secs)) {
751 752 753 754 755
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
756 757 758
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
759 760 761
				goto show_help;
			continue;
		case '?':
762
			if (annotate_browser__search_reverse(browser, delay_secs))
763 764
				goto show_help;
			continue;
765 766 767 768
		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",
769 770 771 772 773
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
774 775
		}
			continue;
776 777
		case K_ENTER:
		case K_RIGHT:
778
			if (browser->selection == NULL)
779
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
780
			else if (browser->selection->offset == -1)
781
				ui_helpline__puts("Actions are only available for assembly lines.");
782 783
			else if (!browser->selection->ins) {
				if (strcmp(browser->selection->name, "retq"))
784 785
					goto show_sup_ins;
				goto out;
786
			} else if (!(annotate_browser__jump(browser) ||
787
				     annotate_browser__callq(browser, evsel, hbt))) {
788 789 790
show_sup_ins:
				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
			}
791
			continue;
792 793
		case K_LEFT:
		case K_ESC:
794 795
		case 'q':
		case CTRL('c'):
796
			goto out;
797 798
		default:
			continue;
799
		}
800 801

		if (nd != NULL)
802
			annotate_browser__set_rb_top(browser, nd);
803 804
	}
out:
805
	ui_browser__hide(&browser->b);
806
	return key;
807 808
}

809
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
810
			     struct hist_browser_timer *hbt)
811
{
812
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
813 814
}

815 816 817 818
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
819 820 821 822 823 824
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
825 826 827 828 829

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

830
		if (!disasm_line__is_valid_jump(dl, sym))
831 832
			continue;

833
		dlt = browser->offsets[dl->ops.target.offset];
834 835 836 837 838 839 840
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

841
		bdlt = disasm_line__browser(dlt);
842 843 844 845
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
846 847 848 849
	}
		
}

850 851 852 853 854 855 856 857 858
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

859 860
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
861
			 struct hist_browser_timer *hbt)
862
{
863
	struct disasm_line *pos, *n;
864
	struct annotation *notes;
865
	size_t size;
866 867 868 869
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
870 871
	struct annotate_browser browser = {
		.b = {
872
			.refresh = annotate_browser__refresh,
873 874
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
875
			.filter  = disasm_line__filter,
876
			.priv	 = &ms,
877
			.use_navkeypressed = true,
878
		},
879
	};
880
	int ret = -1;
881 882
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
883

884
	if (sym == NULL)
885 886
		return -1;

887 888
	size = symbol__size(sym);

889
	if (map->dso->annotate_warned)
890 891
		return -1;

892 893 894 895 896 897
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

898 899 900 901 902 903
	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) {
904
		ui__error("%s", ui_helpline__last_msg);
905
		goto out_free_offsets;
906 907 908 909
	}

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

910
	notes = symbol__annotation(sym);
911
	browser.start = map__rip_2objdump(map, sym->start);
912

913
	list_for_each_entry(pos, &notes->src->source, node) {
914
		struct browser_disasm_line *bpos;
915
		size_t line_len = strlen(pos->line);
916

917 918
		if (browser.b.width < line_len)
			browser.b.width = line_len;
919 920
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
921
		if (pos->offset != -1) {
922
			bpos->idx_asm = browser.nr_asm_entries++;
923 924 925 926 927 928 929 930 931
			/*
			 * 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;
932
		} else
933
			bpos->idx_asm = -1;
934 935
	}

936 937
	annotate_browser__mark_jump_targets(&browser, size);

938
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
939
	browser.max_addr_width = hex_width(sym->end);
940
	browser.jumps_width = width_jumps(browser.max_jump_sources);
941
	browser.nr_events = nr_pcnt;
942
	browser.b.nr_entries = browser.nr_entries;
943
	browser.b.entries = &notes->src->source,
944
	browser.b.width += 18; /* Percentage */
945 946 947 948 949 950

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

	annotate_browser__update_addr_width(&browser);

951
	ret = annotate_browser__run(&browser, evsel, hbt);
952
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
953
		list_del(&pos->node);
954
		disasm_line__free(pos);
955
	}
956 957 958

out_free_offsets:
	free(browser.offsets);
959 960
	return ret;
}
961 962 963

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

965 966 967
/*
 * Keep the entries sorted, they are bsearch'ed
 */
968
static struct annotate_config {
969 970 971 972 973 974 975 976 977 978 979 980 981
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
	ANNOTATE_CFG(show_nr_jumps),
	ANNOTATE_CFG(use_offset),
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
982
	const struct annotate_config *cfg = cfgp;
983 984 985 986

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

987 988
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
989
{
990
	struct annotate_config *cfg;
991 992 993 994 995 996 997
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
998
		      sizeof(struct annotate_config), annotate_config__cmp);
999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010

	if (cfg == NULL)
		return -1;

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

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