annotate.c 25.9 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 30 31 32 33 34 35
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,
};

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

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

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

	return false;
}

73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
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);
}

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

106 107 108 109 110 111 112 113 114 115 116
	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]);
		}
117
	} else {
118
		ui_browser__set_percent_color(browser, 0, current_entry);
119
		slsmg_write_nstring(" ", pcnt_width);
120 121
	}

122
	SLsmg_write_char(' ');
123 124

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

128
	if (!*dl->line)
129
		slsmg_write_nstring(" ", width - pcnt_width);
130 131 132 133
	else if (dl->offset == -1) {
		printed = scnprintf(bf, sizeof(bf), "%*s  ",
				    ab->addr_width, " ");
		slsmg_write_nstring(bf, printed);
134
		slsmg_write_nstring(dl->line, width - printed - pcnt_width + 1);
135
	} else {
136
		u64 addr = dl->offset;
137
		int color = -1;
138

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

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

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

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

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

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

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

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

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

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

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

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

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

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

242 243
	pcnt_width *= ab->nr_events;

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

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

	pcnt_width = 7 * ab->nr_events;
256

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

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

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

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

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

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

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

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

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

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

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

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

	return true;
}

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

431 432 433 434 435 436 437 438
#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);
}

439 440
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
441
				    struct hist_browser_timer *hbt)
442 443
{
	struct map_symbol *ms = browser->b.priv;
444
	struct disasm_line *dl = browser->selection;
445 446 447 448
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;
449
	char title[SYM_TITLE_MAX_SIZE];
450

451
	if (!ins__is_call(dl->ins))
452 453
		return false;

454
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471
	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);
472
	symbol__tui_annotate(target, ms->map, evsel, hbt);
473 474
	sym_title(sym, ms->map, title, sizeof(title));
	ui_browser__show_title(&browser->b, title);
475 476 477
	return true;
}

478 479 480
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
481 482 483 484
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
485
	struct disasm_line *pos;
486 487 488 489 490

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
491
		if (!disasm_line__filter(&browser->b, &pos->node))
492 493 494 495 496 497 498 499
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
500
	struct disasm_line *dl = browser->selection;
501
	s64 idx;
502

503
	if (!ins__is_jump(dl->ins))
504 505
		return false;

506
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
507
	if (dl == NULL) {
508 509 510 511
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

512
	annotate_browser__set_top(browser, dl, idx);
513 514 515 516
	
	return true;
}

517 518 519
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
520 521 522 523
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
524
	struct disasm_line *pos = browser->selection;
525 526 527

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
528
		if (disasm_line__filter(&browser->b, &pos->node))
529 530 531 532 533 534 535 536 537 538 539 540 541
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
542
	struct disasm_line *dl;
543 544
	s64 idx;

545 546
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
547 548 549 550
		ui_helpline__puts("String not found!");
		return false;
	}

551
	annotate_browser__set_top(browser, dl, idx);
552 553 554 555
	browser->searching_backwards = false;
	return true;
}

556 557 558
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
559 560 561 562
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
563
	struct disasm_line *pos = browser->selection;
564 565 566

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
567
		if (disasm_line__filter(&browser->b, &pos->node))
568 569 570 571 572 573 574 575 576 577 578 579 580
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
581
	struct disasm_line *dl;
582 583
	s64 idx;

584 585
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
586 587 588 589
		ui_helpline__puts("String not found!");
		return false;
	}

590
	annotate_browser__set_top(browser, dl, idx);
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 634 635 636 637 638 639 640 641 642
	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);
}

643 644 645 646 647 648 649 650 651 652 653 654 655
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;
}

656 657
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
658
				 struct hist_browser_timer *hbt)
659 660
{
	struct rb_node *nd = NULL;
661
	struct map_symbol *ms = browser->b.priv;
662
	struct symbol *sym = ms->sym;
663
	const char *help = "Press 'h' for help on key bindings";
664
	int delay_secs = hbt ? hbt->refresh : 0;
665
	int key;
666
	char title[SYM_TITLE_MAX_SIZE];
667

668 669
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
670
		return -1;
671

672
	annotate_browser__calc_percent(browser, evsel);
673

674 675 676
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
677
	}
678

679
	nd = browser->curr_hot;
680

681
	while (1) {
682
		key = ui_browser__run(&browser->b, delay_secs);
683

684
		if (delay_secs != 0) {
685
			annotate_browser__calc_percent(browser, evsel);
686 687 688 689 690 691 692 693 694
			/*
			 * 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;
		}

695
		switch (key) {
696
		case K_TIMER:
697 698
			if (hbt)
				hbt->timer(hbt->arg);
699 700

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

		if (nd != NULL)
813
			annotate_browser__set_rb_top(browser, nd);
814 815
	}
out:
816
	ui_browser__hide(&browser->b);
817
	return key;
818 819
}

820
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
821
			     struct hist_browser_timer *hbt)
822
{
823
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
824 825
}

826 827 828 829
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
830 831 832 833 834 835
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
836 837 838 839 840

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

841
		if (!disasm_line__is_valid_jump(dl, sym))
842 843
			continue;

844
		dlt = browser->offsets[dl->ops.target.offset];
845 846 847 848 849 850 851
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

852
		bdlt = disasm_line__browser(dlt);
853 854 855 856
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
857 858 859 860
	}
		
}

861 862 863 864 865 866 867 868 869
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

870 871
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
872
			 struct hist_browser_timer *hbt)
873
{
874
	struct disasm_line *pos, *n;
875
	struct annotation *notes;
876
	size_t size;
877 878 879 880
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
881 882
	struct annotate_browser browser = {
		.b = {
883
			.refresh = annotate_browser__refresh,
884 885
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
886
			.filter  = disasm_line__filter,
887
			.priv	 = &ms,
888
			.use_navkeypressed = true,
889
		},
890
	};
891
	int ret = -1;
892 893
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
894

895
	if (sym == NULL)
896 897
		return -1;

898 899
	size = symbol__size(sym);

900
	if (map->dso->annotate_warned)
901 902
		return -1;

903 904 905 906 907 908
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

909 910 911 912 913 914
	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) {
915
		ui__error("%s", ui_helpline__last_msg);
916
		goto out_free_offsets;
917 918 919 920
	}

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

921
	notes = symbol__annotation(sym);
922
	browser.start = map__rip_2objdump(map, sym->start);
923

924
	list_for_each_entry(pos, &notes->src->source, node) {
925
		struct browser_disasm_line *bpos;
926
		size_t line_len = strlen(pos->line);
927

928 929
		if (browser.b.width < line_len)
			browser.b.width = line_len;
930 931
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
932
		if (pos->offset != -1) {
933
			bpos->idx_asm = browser.nr_asm_entries++;
934 935 936 937 938 939 940 941 942
			/*
			 * 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;
943
		} else
944
			bpos->idx_asm = -1;
945 946
	}

947 948
	annotate_browser__mark_jump_targets(&browser, size);

949
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
950
	browser.max_addr_width = hex_width(sym->end);
951
	browser.jumps_width = width_jumps(browser.max_jump_sources);
952
	browser.nr_events = nr_pcnt;
953
	browser.b.nr_entries = browser.nr_entries;
954
	browser.b.entries = &notes->src->source,
955
	browser.b.width += 18; /* Percentage */
956 957 958 959 960 961

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

	annotate_browser__update_addr_width(&browser);

962
	ret = annotate_browser__run(&browser, evsel, hbt);
963
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
964
		list_del(&pos->node);
965
		disasm_line__free(pos);
966
	}
967 968 969

out_free_offsets:
	free(browser.offsets);
970 971
	return ret;
}
972 973 974

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

976 977 978
/*
 * Keep the entries sorted, they are bsearch'ed
 */
979
static struct annotate_config {
980 981 982 983 984 985 986 987 988 989 990 991 992
	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)
{
993
	const struct annotate_config *cfg = cfgp;
994 995 996 997

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

998 999
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1000
{
1001
	struct annotate_config *cfg;
1002 1003 1004 1005 1006 1007 1008
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1009
		      sizeof(struct annotate_config), annotate_config__cmp);
1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021

	if (cfg == NULL)
		return -1;

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

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