annotate.c 31.3 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 "../../util/evlist.h"
13
#include <inttypes.h>
14
#include <pthread.h>
15
#include <linux/kernel.h>
16
#include <sys/ttydefaults.h>
17

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

23 24 25
#define IPC_WIDTH 6
#define CYCLES_WIDTH 6

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

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

50 51
struct arch;

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

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

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

	return false;
}

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

110 111 112 113 114 115 116 117 118
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;
}

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

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

139 140 141 142 143 144 145 146
	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;
	}

147
	if (dl->offset != -1 && percent_max != 0.0) {
148 149 150 151 152 153 154 155 156 157
		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);
158
			}
159
		}
160
	} else {
161
		ui_browser__set_percent_color(browser, 0, current_entry);
162 163 164 165 166

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

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

185
	SLsmg_write_char(' ');
186 187

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

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

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

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

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

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

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

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

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

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

	return true;
}

276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
	struct disasm_line *pos = list_prev_entry(cursor, node);
	const char *name;

	if (!pos)
		return false;

	if (ins__is_lock(&pos->ins))
		name = pos->ops.locked.ins.name;
	else
		name = pos->ins.name;

	if (!name || !cursor->ins.name)
		return false;

	return ins__is_fused(ab->arch, name, cursor->ins.name);
}

295
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
296 297
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
298 299
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
300
	unsigned int from, to;
301 302
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
303
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
304 305 306 307

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

309
	if (!disasm_line__is_valid_jump(cursor, sym))
310
		return;
311

312 313 314
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
315

316 317
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
318

319
	if (annotate_browser__opts.hide_src_code) {
320
		from = bcursor->idx_asm;
321 322
		to = btarget->idx_asm;
	} else {
323
		from = (u64)bcursor->idx;
324 325 326
		to = (u64)btarget->idx;
	}

327
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
328 329
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
330 331 332 333 334 335 336

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
				       pcnt_width + 3 + ab->addr_width,
				       from - 1,
				       to > from ? true : false);
	}
337 338 339 340
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
341
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
342
	int ret = ui_browser__list_head_refresh(browser);
343
	int pcnt_width = annotate_browser__pcnt_width(ab);
344

345
	if (annotate_browser__opts.jump_arrows)
346
		annotate_browser__draw_current_jump(browser);
347

348
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
349
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
350 351 352
	return ret;
}

353 354 355 356 357 358
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++) {
359
		if (a->samples[i].percent == b->samples[i].percent)
360
			continue;
361
		return a->samples[i].percent < b->samples[i].percent;
362 363 364 365 366 367
	}
	return 0;
}

static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
				   int nr_events)
368
{
369
	struct rb_node **p = &root->rb_node;
370
	struct rb_node *parent = NULL;
371
	struct browser_disasm_line *l;
372 373 374

	while (*p != NULL) {
		parent = *p;
375
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
376 377

		if (disasm__cmp(bdl, l, nr_events))
378 379 380 381
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
382 383
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
384 385
}

386
static void annotate_browser__set_top(struct annotate_browser *browser,
387
				      struct disasm_line *pos, u32 idx)
388 389 390
{
	unsigned back;

391 392 393
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
394

395
	while (browser->b.top_idx != 0 && back != 0) {
396
		pos = list_entry(pos->node.prev, struct disasm_line, node);
397

398
		if (disasm_line__filter(&browser->b, &pos->node))
399 400
			continue;

401
		--browser->b.top_idx;
402 403 404
		--back;
	}

405 406
	browser->b.top = pos;
	browser->b.navkeypressed = true;
407 408 409 410 411
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
412
	struct browser_disasm_line *bpos;
413
	struct disasm_line *pos;
414
	u32 idx;
415

416 417
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
418
	idx = bpos->idx;
419
	if (annotate_browser__opts.hide_src_code)
420 421
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
422
	browser->curr_hot = nd;
423 424
}

425
static void annotate_browser__calc_percent(struct annotate_browser *browser,
426
					   struct perf_evsel *evsel)
427
{
428 429
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
430
	struct annotation *notes = symbol__annotation(sym);
431 432
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
433 434 435 436 437 438

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
439
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
440
		const char *path = NULL;
441 442
		double max_percent = 0.0;
		int i;
443 444 445 446 447 448 449 450

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

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

451
		for (i = 0; i < browser->nr_events; i++) {
452 453 454
			u64 nr_samples;

			bpos->samples[i].percent = disasm__calc_percent(notes,
455 456 457
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
458 459
						&path, &nr_samples);
			bpos->samples[i].nr = nr_samples;
460

461 462
			if (max_percent < bpos->samples[i].percent)
				max_percent = bpos->samples[i].percent;
463 464
		}

465
		if (max_percent < 0.01 && pos->ipc == 0) {
466
			RB_CLEAR_NODE(&bpos->rb_node);
467 468
			continue;
		}
469 470
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
471 472 473 474 475 476
	}
	pthread_mutex_unlock(&notes->lock);

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

477 478
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
479
	struct disasm_line *dl;
480
	struct browser_disasm_line *bdl;
481 482 483
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
484
	dl = list_entry(browser->b.top, struct disasm_line, node);
485
	bdl = disasm_line__browser(dl);
486

487
	if (annotate_browser__opts.hide_src_code) {
488 489
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
490 491

		browser->b.nr_entries = browser->nr_entries;
492
		annotate_browser__opts.hide_src_code = false;
493
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
494 495
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
496
	} else {
497
		if (bdl->idx_asm < 0) {
498 499 500 501 502
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

503 504
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
505 506

		browser->b.nr_entries = browser->nr_asm_entries;
507
		annotate_browser__opts.hide_src_code = true;
508
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
509 510
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
511 512 513 514 515
	}

	return true;
}

516 517 518 519 520 521
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;
}

522 523 524 525 526 527 528 529
#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);
}

530 531
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
532
				    struct hist_browser_timer *hbt)
533 534
{
	struct map_symbol *ms = browser->b.priv;
535
	struct disasm_line *dl = browser->selection;
536
	struct annotation *notes;
537 538
	struct addr_map_symbol target = {
		.map = ms->map,
539
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
540
	};
541
	char title[SYM_TITLE_MAX_SIZE];
542

543
	if (!ins__is_call(&dl->ins))
544 545
		return false;

546
	if (map_groups__find_ams(&target) ||
547 548 549
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
550 551 552 553
		ui_helpline__puts("The called function was not found.");
		return true;
	}

554
	notes = symbol__annotation(target.sym);
555 556
	pthread_mutex_lock(&notes->lock);

557
	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
558 559
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
560
			    target.sym->name);
561 562 563 564
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
565 566
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
567
	ui_browser__show_title(&browser->b, title);
568 569 570
	return true;
}

571 572 573
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
574 575 576 577
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
578
	struct disasm_line *pos;
579 580 581 582 583

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
584
		if (!disasm_line__filter(&browser->b, &pos->node))
585 586 587 588 589 590 591 592
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
593
	struct disasm_line *dl = browser->selection;
594
	u64 offset;
595
	s64 idx;
596

597
	if (!ins__is_jump(&dl->ins))
598 599
		return false;

600 601
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
602
	if (dl == NULL) {
603
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
604 605 606
		return true;
	}

607
	annotate_browser__set_top(browser, dl, idx);
608

609 610 611
	return true;
}

612 613 614
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
615 616 617 618
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
619
	struct disasm_line *pos = browser->selection;
620 621 622

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
623
		if (disasm_line__filter(&browser->b, &pos->node))
624 625 626 627 628 629 630 631 632 633 634 635 636
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
637
	struct disasm_line *dl;
638 639
	s64 idx;

640 641
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
642 643 644 645
		ui_helpline__puts("String not found!");
		return false;
	}

646
	annotate_browser__set_top(browser, dl, idx);
647 648 649 650
	browser->searching_backwards = false;
	return true;
}

651 652 653
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
654 655 656 657
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
658
	struct disasm_line *pos = browser->selection;
659 660 661

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
662
		if (disasm_line__filter(&browser->b, &pos->node))
663 664 665 666 667 668 669 670 671 672 673 674 675
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
676
	struct disasm_line *dl;
677 678
	s64 idx;

679 680
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
681 682 683 684
		ui_helpline__puts("String not found!");
		return false;
	}

685
	annotate_browser__set_top(browser, dl, idx);
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 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737
	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);
}

738 739 740 741 742 743 744 745 746 747 748 749 750
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;
}

751 752
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
753
				 struct hist_browser_timer *hbt)
754 755
{
	struct rb_node *nd = NULL;
756
	struct map_symbol *ms = browser->b.priv;
757
	struct symbol *sym = ms->sym;
758
	const char *help = "Press 'h' for help on key bindings";
759
	int delay_secs = hbt ? hbt->refresh : 0;
760
	int key;
761
	char title[SYM_TITLE_MAX_SIZE];
762

763 764
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
765
		return -1;
766

767
	annotate_browser__calc_percent(browser, evsel);
768

769 770 771
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
772
	}
773

774
	nd = browser->curr_hot;
775

776
	while (1) {
777
		key = ui_browser__run(&browser->b, delay_secs);
778

779
		if (delay_secs != 0) {
780
			annotate_browser__calc_percent(browser, evsel);
781 782 783 784 785 786 787 788 789
			/*
			 * 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;
		}

790
		switch (key) {
791
		case K_TIMER:
792 793
			if (hbt)
				hbt->timer(hbt->arg);
794 795

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

		if (nd != NULL)
919
			annotate_browser__set_rb_top(browser, nd);
920 921
	}
out:
922
	ui_browser__hide(&browser->b);
923
	return key;
924 925
}

926 927 928
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
929 930 931 932
	/* Set default value for show_total_period.  */
	annotate_browser__opts.show_total_period =
	  symbol_conf.show_total_period;

933 934 935
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

936
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
937
			     struct hist_browser_timer *hbt)
938
{
939 940 941 942
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

943
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
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 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014

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);
}

1015 1016 1017 1018
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
1019 1020 1021 1022 1023 1024
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
1025 1026 1027 1028 1029

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

1030
		if (!disasm_line__is_valid_jump(dl, sym))
1031 1032
			continue;

1033
		dlt = browser->offsets[dl->ops.target.offset];
1034 1035 1036 1037 1038 1039 1040
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

1041
		bdlt = disasm_line__browser(dlt);
1042 1043 1044 1045
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
1046 1047 1048
	}
}

1049 1050 1051 1052 1053 1054 1055 1056 1057
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1058 1059
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1060
			 struct hist_browser_timer *hbt)
1061
{
1062
	struct disasm_line *pos, *n;
1063
	struct annotation *notes;
1064
	size_t size;
1065 1066 1067 1068
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1069 1070
	struct annotate_browser browser = {
		.b = {
1071
			.refresh = annotate_browser__refresh,
1072 1073
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1074
			.filter  = disasm_line__filter,
1075
			.priv	 = &ms,
1076
			.use_navkeypressed = true,
1077
		},
1078
	};
1079
	int ret = -1, err;
1080 1081
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1082

1083
	if (sym == NULL)
1084 1085
		return -1;

1086 1087
	size = symbol__size(sym);

1088
	if (map->dso->annotate_warned)
1089 1090
		return -1;

1091 1092 1093 1094 1095 1096
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1097 1098
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
1099 1100
		sizeof_bdl += sizeof(struct disasm_line_samples) *
		  (nr_pcnt - 1);
1101 1102
	}

1103
	err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
1104 1105
				  sizeof_bdl, &browser.arch,
				  perf_evsel__env_cpuid(evsel));
1106 1107 1108 1109
	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);
1110
		goto out_free_offsets;
1111 1112
	}

1113
	ui_helpline__push("Press ESC to exit");
1114

1115
	notes = symbol__annotation(sym);
1116
	browser.start = map__rip_2objdump(map, sym->start);
1117

1118
	list_for_each_entry(pos, &notes->src->source, node) {
1119
		struct browser_disasm_line *bpos;
1120
		size_t line_len = strlen(pos->line);
1121

1122 1123
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1124 1125
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
1126
		if (pos->offset != -1) {
1127
			bpos->idx_asm = browser.nr_asm_entries++;
1128 1129 1130 1131 1132 1133 1134 1135 1136
			/*
			 * 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;
1137
		} else
1138
			bpos->idx_asm = -1;
1139 1140
	}

1141
	annotate_browser__mark_jump_targets(&browser, size);
1142
	annotate__compute_ipc(&browser, size, sym);
1143

1144
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1145
	browser.max_addr_width = hex_width(sym->end);
1146
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1147
	browser.nr_events = nr_pcnt;
1148
	browser.b.nr_entries = browser.nr_entries;
1149
	browser.b.entries = &notes->src->source,
1150
	browser.b.width += 18; /* Percentage */
1151 1152 1153 1154 1155 1156

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

	annotate_browser__update_addr_width(&browser);

1157
	ret = annotate_browser__run(&browser, evsel, hbt);
1158
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1159
		list_del(&pos->node);
1160
		disasm_line__free(pos);
1161
	}
1162 1163 1164

out_free_offsets:
	free(browser.offsets);
1165 1166
	return ret;
}
1167 1168 1169

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

1171 1172 1173
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1174
static struct annotate_config {
1175 1176 1177 1178 1179
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1180
	ANNOTATE_CFG(show_linenr),
1181
	ANNOTATE_CFG(show_nr_jumps),
1182
	ANNOTATE_CFG(show_total_period),
1183
	ANNOTATE_CFG(use_offset),
1184 1185 1186 1187 1188 1189
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1190
	const struct annotate_config *cfg = cfgp;
1191 1192 1193 1194

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

1195 1196
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1197
{
1198
	struct annotate_config *cfg;
1199 1200 1201 1202 1203 1204 1205
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1206
		      sizeof(struct annotate_config), annotate_config__cmp);
1207 1208

	if (cfg == NULL)
1209 1210 1211
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1212 1213 1214 1215 1216 1217 1218
	return 0;
}

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