annotate.c 30.7 KB
Newer Older
1
#include "../../util/util.h"
2 3
#include "../browser.h"
#include "../helpline.h"
4 5
#include "../ui.h"
#include "../util.h"
6 7 8 9
#include "../../util/annotate.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/symbol.h"
10
#include "../../util/evsel.h"
11
#include "../../util/config.h"
12
#include <inttypes.h>
13
#include <pthread.h>
14
#include <linux/kernel.h>
15
#include <sys/ttydefaults.h>
16

17 18 19 20 21
struct disasm_line_samples {
	double		percent;
	u64		nr;
};

22 23 24
#define IPC_WIDTH 6
#define CYCLES_WIDTH 6

25
struct browser_disasm_line {
26 27 28 29
	struct rb_node			rb_node;
	u32				idx;
	int				idx_asm;
	int				jump_sources;
30 31 32 33
	/*
	 * actual length of this array is saved on the nr_events field
	 * of the struct annotate_browser
	 */
34
	struct disasm_line_samples	samples[1];
35 36
};

37 38 39 40
static struct annotate_browser_opt {
	bool hide_src_code,
	     use_offset,
	     jump_arrows,
41
	     show_linenr,
42 43
	     show_nr_jumps,
	     show_total_period;
44 45 46 47 48
} annotate_browser__opts = {
	.use_offset	= true,
	.jump_arrows	= true,
};

49 50
struct arch;

51 52 53
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
54
	struct rb_node	  *curr_hot;
55
	struct disasm_line  *selection;
56
	struct disasm_line  **offsets;
57
	struct arch	    *arch;
58
	int		    nr_events;
59
	u64		    start;
60 61
	int		    nr_asm_entries;
	int		    nr_entries;
62 63
	int		    max_jump_sources;
	int		    nr_jumps;
64
	bool		    searching_backwards;
65
	bool		    have_cycles;
66
	u8		    addr_width;
67 68
	u8		    jumps_width;
	u8		    target_width;
69 70
	u8		    min_addr_width;
	u8		    max_addr_width;
71
	char		    search_bf[128];
72 73
};

74
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
75
{
76
	return (struct browser_disasm_line *)(dl + 1);
77 78
}

79 80
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
81
{
82
	if (annotate_browser__opts.hide_src_code) {
83 84
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
85 86 87 88 89
	}

	return false;
}

90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
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);
}

109 110 111 112 113 114 115 116 117
static int annotate_browser__pcnt_width(struct annotate_browser *ab)
{
	int w = 7 * ab->nr_events;

	if (ab->have_cycles)
		w += IPC_WIDTH + CYCLES_WIDTH;
	return w;
}

118
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
119
{
120
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
121
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
122
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
123
	bool current_entry = ui_browser__is_current_entry(browser, row);
124
	bool change_color = (!annotate_browser__opts.hide_src_code &&
125 126 127
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
128
	int i, pcnt_width = annotate_browser__pcnt_width(ab);
129
	double percent_max = 0.0;
130
	char bf[256];
131
	bool show_title = false;
132

133
	for (i = 0; i < ab->nr_events; i++) {
134 135
		if (bdl->samples[i].percent > percent_max)
			percent_max = bdl->samples[i].percent;
136 137
	}

138 139 140 141 142 143 144 145
	if ((row == 0) && (dl->offset == -1 || percent_max == 0.0)) {
		if (ab->have_cycles) {
			if (dl->ipc == 0.0 && dl->cycles == 0)
				show_title = true;
		} else
			show_title = true;
	}

146
	if (dl->offset != -1 && percent_max != 0.0) {
147 148 149 150 151 152 153 154 155 156
		for (i = 0; i < ab->nr_events; i++) {
			ui_browser__set_percent_color(browser,
						bdl->samples[i].percent,
						current_entry);
			if (annotate_browser__opts.show_total_period) {
				ui_browser__printf(browser, "%6" PRIu64 " ",
						   bdl->samples[i].nr);
			} else {
				ui_browser__printf(browser, "%6.2f ",
						   bdl->samples[i].percent);
157
			}
158
		}
159
	} else {
160
		ui_browser__set_percent_color(browser, 0, current_entry);
161 162 163 164 165

		if (!show_title)
			ui_browser__write_nstring(browser, " ", 7 * ab->nr_events);
		else
			ui_browser__printf(browser, "%*s", 7, "Percent");
166 167 168
	}
	if (ab->have_cycles) {
		if (dl->ipc)
169
			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
170
		else if (!show_title)
171
			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
172 173 174
		else
			ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");

175
		if (dl->cycles)
176 177
			ui_browser__printf(browser, "%*" PRIu64 " ",
					   CYCLES_WIDTH - 1, dl->cycles);
178
		else if (!show_title)
179
			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
180 181
		else
			ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
182 183
	}

184
	SLsmg_write_char(' ');
185 186

	/* The scroll bar isn't being used */
187
	if (!browser->navkeypressed)
188 189
		width += 1;

190
	if (!*dl->line)
191
		ui_browser__write_nstring(browser, " ", width - pcnt_width);
192
	else if (dl->offset == -1) {
193 194 195 196 197
		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  ",
198
				    ab->addr_width, " ");
199 200
		ui_browser__write_nstring(browser, bf, printed);
		ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width + 1);
201
	} else {
202
		u64 addr = dl->offset;
203
		int color = -1;
204

205
		if (!annotate_browser__opts.use_offset)
206 207
			addr += ab->start;

208
		if (!annotate_browser__opts.use_offset) {
209
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
210
		} else {
211
			if (bdl->jump_sources) {
212
				if (annotate_browser__opts.show_nr_jumps) {
213 214 215 216 217 218
					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);
219
					ui_browser__write_nstring(browser, bf, printed);
220
					ui_browser__set_color(browser, prev);
221 222
				}

223
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
224
						    ab->target_width, addr);
225
			} else {
226 227
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
228 229
			}
		}
230

231
		if (change_color)
232
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
233
		ui_browser__write_nstring(browser, bf, printed);
234
		if (change_color)
235
			ui_browser__set_color(browser, color);
236 237
		if (dl->ins.ops && dl->ins.ops->scnprintf) {
			if (ins__is_jump(&dl->ins)) {
238
				bool fwd = dl->ops.target.offset > dl->offset;
239

240
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
241
								    SLSMG_UARROW_CHAR);
242
				SLsmg_write_char(' ');
243
			} else if (ins__is_call(&dl->ins)) {
244
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
245
				SLsmg_write_char(' ');
246
			} else if (ins__is_ret(&dl->ins)) {
247 248
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
				SLsmg_write_char(' ');
249
			} else {
250
				ui_browser__write_nstring(browser, " ", 2);
251
			}
252
		} else {
253
			ui_browser__write_nstring(browser, " ", 2);
254
		}
255

256
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
257
		ui_browser__write_nstring(browser, bf, width - pcnt_width - 3 - printed);
258
	}
259

260
	if (current_entry)
261
		ab->selection = dl;
262 263
}

264 265
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
266
	if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
267
	    || !disasm_line__has_offset(dl)
268 269
	    || dl->ops.target.offset < 0
	    || dl->ops.target.offset >= (s64)symbol__size(sym))
270 271 272 273 274
		return false;

	return true;
}

275
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
276 277
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
278 279
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
280
	unsigned int from, to;
281 282
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
283
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
284 285 286 287

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

289
	if (!disasm_line__is_valid_jump(cursor, sym))
290
		return;
291

292 293 294
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
295

296 297
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
298

299
	if (annotate_browser__opts.hide_src_code) {
300
		from = bcursor->idx_asm;
301 302
		to = btarget->idx_asm;
	} else {
303
		from = (u64)bcursor->idx;
304 305 306
		to = (u64)btarget->idx;
	}

307
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
308 309
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
310 311 312 313
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
314
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
315
	int ret = ui_browser__list_head_refresh(browser);
316
	int pcnt_width = annotate_browser__pcnt_width(ab);
317

318
	if (annotate_browser__opts.jump_arrows)
319
		annotate_browser__draw_current_jump(browser);
320

321
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
322
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
323 324 325
	return ret;
}

326 327 328 329 330 331
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++) {
332
		if (a->samples[i].percent == b->samples[i].percent)
333
			continue;
334
		return a->samples[i].percent < b->samples[i].percent;
335 336 337 338 339 340
	}
	return 0;
}

static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
				   int nr_events)
341
{
342
	struct rb_node **p = &root->rb_node;
343
	struct rb_node *parent = NULL;
344
	struct browser_disasm_line *l;
345 346 347

	while (*p != NULL) {
		parent = *p;
348
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
349 350

		if (disasm__cmp(bdl, l, nr_events))
351 352 353 354
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
355 356
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
357 358
}

359
static void annotate_browser__set_top(struct annotate_browser *browser,
360
				      struct disasm_line *pos, u32 idx)
361 362 363
{
	unsigned back;

364 365 366
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
367

368
	while (browser->b.top_idx != 0 && back != 0) {
369
		pos = list_entry(pos->node.prev, struct disasm_line, node);
370

371
		if (disasm_line__filter(&browser->b, &pos->node))
372 373
			continue;

374
		--browser->b.top_idx;
375 376 377
		--back;
	}

378 379
	browser->b.top = pos;
	browser->b.navkeypressed = true;
380 381 382 383 384
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
385
	struct browser_disasm_line *bpos;
386
	struct disasm_line *pos;
387
	u32 idx;
388

389 390
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
391
	idx = bpos->idx;
392
	if (annotate_browser__opts.hide_src_code)
393 394
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
395
	browser->curr_hot = nd;
396 397
}

398
static void annotate_browser__calc_percent(struct annotate_browser *browser,
399
					   struct perf_evsel *evsel)
400
{
401 402
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
403
	struct annotation *notes = symbol__annotation(sym);
404 405
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
406 407 408 409 410 411

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
412
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
413
		const char *path = NULL;
414 415
		double max_percent = 0.0;
		int i;
416 417 418 419 420 421 422 423

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

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

424
		for (i = 0; i < browser->nr_events; i++) {
425 426 427
			u64 nr_samples;

			bpos->samples[i].percent = disasm__calc_percent(notes,
428 429 430
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
431 432
						&path, &nr_samples);
			bpos->samples[i].nr = nr_samples;
433

434 435
			if (max_percent < bpos->samples[i].percent)
				max_percent = bpos->samples[i].percent;
436 437
		}

438
		if (max_percent < 0.01 && pos->ipc == 0) {
439
			RB_CLEAR_NODE(&bpos->rb_node);
440 441
			continue;
		}
442 443
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
444 445 446 447 448 449
	}
	pthread_mutex_unlock(&notes->lock);

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

450 451
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
452
	struct disasm_line *dl;
453
	struct browser_disasm_line *bdl;
454 455 456
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
457
	dl = list_entry(browser->b.top, struct disasm_line, node);
458
	bdl = disasm_line__browser(dl);
459

460
	if (annotate_browser__opts.hide_src_code) {
461 462
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
463 464

		browser->b.nr_entries = browser->nr_entries;
465
		annotate_browser__opts.hide_src_code = false;
466
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
467 468
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
469
	} else {
470
		if (bdl->idx_asm < 0) {
471 472 473 474 475
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

476 477
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
478 479

		browser->b.nr_entries = browser->nr_asm_entries;
480
		annotate_browser__opts.hide_src_code = true;
481
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
482 483
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
484 485 486 487 488
	}

	return true;
}

489 490 491 492 493 494
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;
}

495 496 497 498 499 500 501 502
#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);
}

503 504
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
505
				    struct hist_browser_timer *hbt)
506 507
{
	struct map_symbol *ms = browser->b.priv;
508
	struct disasm_line *dl = browser->selection;
509
	struct annotation *notes;
510 511
	struct addr_map_symbol target = {
		.map = ms->map,
512
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
513
	};
514
	char title[SYM_TITLE_MAX_SIZE];
515

516
	if (!ins__is_call(&dl->ins))
517 518
		return false;

519
	if (map_groups__find_ams(&target) ||
520 521 522
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
523 524 525 526
		ui_helpline__puts("The called function was not found.");
		return true;
	}

527
	notes = symbol__annotation(target.sym);
528 529
	pthread_mutex_lock(&notes->lock);

530
	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
531 532
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
533
			    target.sym->name);
534 535 536 537
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
538 539
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
540
	ui_browser__show_title(&browser->b, title);
541 542 543
	return true;
}

544 545 546
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
547 548 549 550
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
551
	struct disasm_line *pos;
552 553 554 555 556

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
557
		if (!disasm_line__filter(&browser->b, &pos->node))
558 559 560 561 562 563 564 565
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
566
	struct disasm_line *dl = browser->selection;
567
	u64 offset;
568
	s64 idx;
569

570
	if (!ins__is_jump(&dl->ins))
571 572
		return false;

573 574
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
575
	if (dl == NULL) {
576
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
577 578 579
		return true;
	}

580
	annotate_browser__set_top(browser, dl, idx);
581

582 583 584
	return true;
}

585 586 587
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
588 589 590 591
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
592
	struct disasm_line *pos = browser->selection;
593 594 595

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
596
		if (disasm_line__filter(&browser->b, &pos->node))
597 598 599 600 601 602 603 604 605 606 607 608 609
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
610
	struct disasm_line *dl;
611 612
	s64 idx;

613 614
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
615 616 617 618
		ui_helpline__puts("String not found!");
		return false;
	}

619
	annotate_browser__set_top(browser, dl, idx);
620 621 622 623
	browser->searching_backwards = false;
	return true;
}

624 625 626
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
627 628 629 630
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
631
	struct disasm_line *pos = browser->selection;
632 633 634

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
635
		if (disasm_line__filter(&browser->b, &pos->node))
636 637 638 639 640 641 642 643 644 645 646 647 648
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
649
	struct disasm_line *dl;
650 651
	s64 idx;

652 653
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
654 655 656 657
		ui_helpline__puts("String not found!");
		return false;
	}

658
	annotate_browser__set_top(browser, dl, idx);
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710
	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);
}

711 712 713 714 715 716 717 718 719 720 721 722 723
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;
}

724 725
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
726
				 struct hist_browser_timer *hbt)
727 728
{
	struct rb_node *nd = NULL;
729
	struct map_symbol *ms = browser->b.priv;
730
	struct symbol *sym = ms->sym;
731
	const char *help = "Press 'h' for help on key bindings";
732
	int delay_secs = hbt ? hbt->refresh : 0;
733
	int key;
734
	char title[SYM_TITLE_MAX_SIZE];
735

736 737
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
738
		return -1;
739

740
	annotate_browser__calc_percent(browser, evsel);
741

742 743 744
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
745
	}
746

747
	nd = browser->curr_hot;
748

749
	while (1) {
750
		key = ui_browser__run(&browser->b, delay_secs);
751

752
		if (delay_secs != 0) {
753
			annotate_browser__calc_percent(browser, evsel);
754 755 756 757 758 759 760 761 762
			/*
			 * 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;
		}

763
		switch (key) {
764
		case K_TIMER:
765 766
			if (hbt)
				hbt->timer(hbt->arg);
767 768

			if (delay_secs != 0)
769
				symbol__annotate_decay_histogram(sym, evsel->idx);
770
			continue;
771
		case K_TAB:
772 773 774
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
775
					nd = rb_last(&browser->entries);
776
			} else
777
				nd = browser->curr_hot;
778
			break;
779
		case K_UNTAB:
780
			if (nd != NULL) {
781 782
				nd = rb_next(nd);
				if (nd == NULL)
783
					nd = rb_first(&browser->entries);
784
			} else
785
				nd = browser->curr_hot;
786
			break;
787
		case K_F1:
788
		case 'h':
789
			ui_browser__help_window(&browser->b,
790 791 792
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
793 794
		"ENTER         Go to target\n"
		"ESC           Exit\n"
795
		"H             Cycle thru hottest instructions\n"
796 797 798 799 800
		"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"
801
		"t             Toggle total period view\n"
802
		"/             Search string\n"
803
		"k             Toggle line numbers\n"
804
		"r             Run available scripts\n"
805
		"?             Search string backwards\n");
806
			continue;
807 808 809 810 811
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
812 813 814 815
		case 'k':
			annotate_browser__opts.show_linenr =
				!annotate_browser__opts.show_linenr;
			break;
816
		case 'H':
817
			nd = browser->curr_hot;
818
			break;
819
		case 's':
820
			if (annotate_browser__toggle_source(browser))
821 822
				ui_helpline__puts(help);
			continue;
823
		case 'o':
824
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
825
			annotate_browser__update_addr_width(browser);
826
			continue;
827
		case 'j':
828
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
829
			continue;
830
		case 'J':
831
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
832
			annotate_browser__update_addr_width(browser);
833
			continue;
834
		case '/':
835
			if (annotate_browser__search(browser, delay_secs)) {
836 837 838 839 840
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
841 842 843
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
844 845 846
				goto show_help;
			continue;
		case '?':
847
			if (annotate_browser__search_reverse(browser, delay_secs))
848 849
				goto show_help;
			continue;
850 851 852 853
		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",
854 855 856 857 858
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
859 860
		}
			continue;
861 862
		case K_ENTER:
		case K_RIGHT:
863
			if (browser->selection == NULL)
864
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
865
			else if (browser->selection->offset == -1)
866
				ui_helpline__puts("Actions are only available for assembly lines.");
867
			else if (!browser->selection->ins.ops)
868
				goto show_sup_ins;
869
			else if (ins__is_ret(&browser->selection->ins))
870
				goto out;
871
			else if (!(annotate_browser__jump(browser) ||
872
				     annotate_browser__callq(browser, evsel, hbt))) {
873
show_sup_ins:
874
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
875
			}
876
			continue;
877 878 879 880 881
		case 't':
			annotate_browser__opts.show_total_period =
			  !annotate_browser__opts.show_total_period;
			annotate_browser__update_addr_width(browser);
			continue;
882 883
		case K_LEFT:
		case K_ESC:
884 885
		case 'q':
		case CTRL('c'):
886
			goto out;
887 888
		default:
			continue;
889
		}
890 891

		if (nd != NULL)
892
			annotate_browser__set_rb_top(browser, nd);
893 894
	}
out:
895
	ui_browser__hide(&browser->b);
896
	return key;
897 898
}

899 900 901
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
902 903 904 905
	/* Set default value for show_total_period.  */
	annotate_browser__opts.show_total_period =
	  symbol_conf.show_total_period;

906 907 908
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

909
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
910
			     struct hist_browser_timer *hbt)
911
{
912 913 914 915
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

916
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
917 918
}

919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987

static unsigned count_insn(struct annotate_browser *browser, u64 start, u64 end)
{
	unsigned n_insn = 0;
	u64 offset;

	for (offset = start; offset <= end; offset++) {
		if (browser->offsets[offset])
			n_insn++;
	}
	return n_insn;
}

static void count_and_fill(struct annotate_browser *browser, u64 start, u64 end,
			   struct cyc_hist *ch)
{
	unsigned n_insn;
	u64 offset;

	n_insn = count_insn(browser, start, end);
	if (n_insn && ch->num && ch->cycles) {
		float ipc = n_insn / ((double)ch->cycles / (double)ch->num);

		/* Hide data when there are too many overlaps. */
		if (ch->reset >= 0x7fff || ch->reset >= ch->num / 2)
			return;

		for (offset = start; offset <= end; offset++) {
			struct disasm_line *dl = browser->offsets[offset];

			if (dl)
				dl->ipc = ipc;
		}
	}
}

/*
 * This should probably be in util/annotate.c to share with the tty
 * annotate, but right now we need the per byte offsets arrays,
 * which are only here.
 */
static void annotate__compute_ipc(struct annotate_browser *browser, size_t size,
			   struct symbol *sym)
{
	u64 offset;
	struct annotation *notes = symbol__annotation(sym);

	if (!notes->src || !notes->src->cycles_hist)
		return;

	pthread_mutex_lock(&notes->lock);
	for (offset = 0; offset < size; ++offset) {
		struct cyc_hist *ch;

		ch = &notes->src->cycles_hist[offset];
		if (ch && ch->cycles) {
			struct disasm_line *dl;

			if (ch->have_start)
				count_and_fill(browser, ch->start, offset, ch);
			dl = browser->offsets[offset];
			if (dl && ch->num_aggr)
				dl->cycles = ch->cycles_aggr / ch->num_aggr;
			browser->have_cycles = true;
		}
	}
	pthread_mutex_unlock(&notes->lock);
}

988 989 990 991
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
992 993 994 995 996 997
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
998 999 1000 1001 1002

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

1003
		if (!disasm_line__is_valid_jump(dl, sym))
1004 1005
			continue;

1006
		dlt = browser->offsets[dl->ops.target.offset];
1007 1008 1009 1010 1011 1012 1013
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

1014
		bdlt = disasm_line__browser(dlt);
1015 1016 1017 1018
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
1019 1020 1021
	}
}

1022 1023 1024 1025 1026 1027 1028 1029 1030
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1031 1032
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1033
			 struct hist_browser_timer *hbt)
1034
{
1035
	struct disasm_line *pos, *n;
1036
	struct annotation *notes;
1037
	size_t size;
1038 1039 1040 1041
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1042 1043
	struct annotate_browser browser = {
		.b = {
1044
			.refresh = annotate_browser__refresh,
1045 1046
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1047
			.filter  = disasm_line__filter,
1048
			.priv	 = &ms,
1049
			.use_navkeypressed = true,
1050
		},
1051
	};
1052
	int ret = -1, err;
1053 1054
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1055

1056
	if (sym == NULL)
1057 1058
		return -1;

1059 1060
	size = symbol__size(sym);

1061
	if (map->dso->annotate_warned)
1062 1063
		return -1;

1064 1065 1066 1067 1068 1069
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1070 1071
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
1072 1073
		sizeof_bdl += sizeof(struct disasm_line_samples) *
		  (nr_pcnt - 1);
1074 1075
	}

1076 1077
	err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
				  sizeof_bdl, &browser.arch);
1078 1079 1080 1081
	if (err) {
		char msg[BUFSIZ];
		symbol__strerror_disassemble(sym, map, err, msg, sizeof(msg));
		ui__error("Couldn't annotate %s:\n%s", sym->name, msg);
1082
		goto out_free_offsets;
1083 1084
	}

1085
	ui_helpline__push("Press ESC to exit");
1086

1087
	notes = symbol__annotation(sym);
1088
	browser.start = map__rip_2objdump(map, sym->start);
1089

1090
	list_for_each_entry(pos, &notes->src->source, node) {
1091
		struct browser_disasm_line *bpos;
1092
		size_t line_len = strlen(pos->line);
1093

1094 1095
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1096 1097
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
1098
		if (pos->offset != -1) {
1099
			bpos->idx_asm = browser.nr_asm_entries++;
1100 1101 1102 1103 1104 1105 1106 1107 1108
			/*
			 * 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;
1109
		} else
1110
			bpos->idx_asm = -1;
1111 1112
	}

1113
	annotate_browser__mark_jump_targets(&browser, size);
1114
	annotate__compute_ipc(&browser, size, sym);
1115

1116
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1117
	browser.max_addr_width = hex_width(sym->end);
1118
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1119
	browser.nr_events = nr_pcnt;
1120
	browser.b.nr_entries = browser.nr_entries;
1121
	browser.b.entries = &notes->src->source,
1122
	browser.b.width += 18; /* Percentage */
1123 1124 1125 1126 1127 1128

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

	annotate_browser__update_addr_width(&browser);

1129
	ret = annotate_browser__run(&browser, evsel, hbt);
1130
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1131
		list_del(&pos->node);
1132
		disasm_line__free(pos);
1133
	}
1134 1135 1136

out_free_offsets:
	free(browser.offsets);
1137 1138
	return ret;
}
1139 1140 1141

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

1143 1144 1145
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1146
static struct annotate_config {
1147 1148 1149 1150 1151
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1152
	ANNOTATE_CFG(show_linenr),
1153
	ANNOTATE_CFG(show_nr_jumps),
1154
	ANNOTATE_CFG(show_total_period),
1155
	ANNOTATE_CFG(use_offset),
1156 1157 1158 1159 1160 1161
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1162
	const struct annotate_config *cfg = cfgp;
1163 1164 1165 1166

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

1167 1168
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1169
{
1170
	struct annotate_config *cfg;
1171 1172 1173 1174 1175 1176 1177
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1178
		      sizeof(struct annotate_config), annotate_config__cmp);
1179 1180

	if (cfg == NULL)
1181 1182 1183
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1184 1185 1186 1187 1188 1189 1190
	return 0;
}

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