annotate.c 32.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 <linux/string.h>
17
#include <sys/ttydefaults.h>
18

19
struct disasm_line_samples {
20 21
	double		      percent;
	struct sym_hist_entry he;
22 23
};

24 25 26
#define IPC_WIDTH 6
#define CYCLES_WIDTH 6

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

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

52 53
struct arch;

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

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

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

	return false;
}

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

112 113
static int annotate_browser__pcnt_width(struct annotate_browser *ab)
{
114
	return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
115
}
116

117 118 119
static int annotate_browser__cycles_width(struct annotate_browser *ab)
{
	return ab->have_cycles ? IPC_WIDTH + CYCLES_WIDTH : 0;
120 121
}

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

138
	for (i = 0; i < ab->nr_events; i++) {
139 140
		if (bdl->samples[i].percent > percent_max)
			percent_max = bdl->samples[i].percent;
141 142
	}

143 144 145 146 147 148 149 150
	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;
	}

151
	if (dl->offset != -1 && percent_max != 0.0) {
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) {
157
				ui_browser__printf(browser, "%11" PRIu64 " ",
158
						   bdl->samples[i].he.period);
159 160 161
			} else if (annotate_browser__opts.show_nr_samples) {
				ui_browser__printf(browser, "%6" PRIu64 " ",
						   bdl->samples[i].he.nr_samples);
162 163 164
			} else {
				ui_browser__printf(browser, "%6.2f ",
						   bdl->samples[i].percent);
165
			}
166
		}
167
	} else {
168
		ui_browser__set_percent_color(browser, 0, current_entry);
169 170

		if (!show_title)
171
			ui_browser__write_nstring(browser, " ", pcnt_width);
172 173
		else {
			ui_browser__printf(browser, "%*s", pcnt_width,
174 175
					   annotate_browser__opts.show_total_period ? "Period" :
					   annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
176
		}
177 178 179
	}
	if (ab->have_cycles) {
		if (dl->ipc)
180
			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->ipc);
181
		else if (!show_title)
182
			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
183 184 185
		else
			ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");

186
		if (dl->cycles)
187 188
			ui_browser__printf(browser, "%*" PRIu64 " ",
					   CYCLES_WIDTH - 1, dl->cycles);
189
		else if (!show_title)
190
			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
191 192
		else
			ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
193 194
	}

195
	SLsmg_write_char(' ');
196 197

	/* The scroll bar isn't being used */
198
	if (!browser->navkeypressed)
199 200
		width += 1;

201
	if (!*dl->line)
202
		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
203
	else if (dl->offset == -1) {
204 205 206 207 208
		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  ",
209
				    ab->addr_width, " ");
210
		ui_browser__write_nstring(browser, bf, printed);
211
		ui_browser__write_nstring(browser, dl->line, width - printed - pcnt_width - cycles_width + 1);
212
	} else {
213
		u64 addr = dl->offset;
214
		int color = -1;
215

216
		if (!annotate_browser__opts.use_offset)
217 218
			addr += ab->start;

219
		if (!annotate_browser__opts.use_offset) {
220
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
221
		} else {
222
			if (bdl->jump_sources) {
223
				if (annotate_browser__opts.show_nr_jumps) {
224 225 226 227 228 229
					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);
230
					ui_browser__write_nstring(browser, bf, printed);
231
					ui_browser__set_color(browser, prev);
232 233
				}

234
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
235
						    ab->target_width, addr);
236
			} else {
237 238
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
239 240
			}
		}
241

242
		if (change_color)
243
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
244
		ui_browser__write_nstring(browser, bf, printed);
245
		if (change_color)
246
			ui_browser__set_color(browser, color);
247 248
		if (dl->ins.ops && dl->ins.ops->scnprintf) {
			if (ins__is_jump(&dl->ins)) {
249
				bool fwd = dl->ops.target.offset > dl->offset;
250

251
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
252
								    SLSMG_UARROW_CHAR);
253
				SLsmg_write_char(' ');
254
			} else if (ins__is_call(&dl->ins)) {
255
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
256
				SLsmg_write_char(' ');
257
			} else if (ins__is_ret(&dl->ins)) {
258 259
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
				SLsmg_write_char(' ');
260
			} else {
261
				ui_browser__write_nstring(browser, " ", 2);
262
			}
263
		} else {
264
			ui_browser__write_nstring(browser, " ", 2);
265
		}
266

267
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
268
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
269
	}
270

271
	if (current_entry)
272
		ab->selection = dl;
273 274
}

275 276
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
277
	if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
278
	    || !disasm_line__has_offset(dl)
279 280
	    || dl->ops.target.offset < 0
	    || dl->ops.target.offset >= (s64)symbol__size(sym))
281 282 283 284 285
		return false;

	return true;
}

286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
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);
}

305
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
306 307
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
308 309
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
310
	unsigned int from, to;
311 312
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
313
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
314 315 316 317

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

319
	if (!disasm_line__is_valid_jump(cursor, sym))
320
		return;
321

322 323 324
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
325

326 327
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
328

329
	if (annotate_browser__opts.hide_src_code) {
330
		from = bcursor->idx_asm;
331 332
		to = btarget->idx_asm;
	} else {
333
		from = (u64)bcursor->idx;
334 335 336
		to = (u64)btarget->idx;
	}

337
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
338 339
	__ui_browser__line_arrow(browser, pcnt_width + 2 + ab->addr_width,
				 from, to);
340 341 342 343 344 345 346

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
				       pcnt_width + 3 + ab->addr_width,
				       from - 1,
				       to > from ? true : false);
	}
347 348 349 350
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
351
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
352
	int ret = ui_browser__list_head_refresh(browser);
353
	int pcnt_width = annotate_browser__pcnt_width(ab);
354

355
	if (annotate_browser__opts.jump_arrows)
356
		annotate_browser__draw_current_jump(browser);
357

358
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
359
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
360 361 362
	return ret;
}

363 364 365 366 367 368
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++) {
369
		if (a->samples[i].percent == b->samples[i].percent)
370
			continue;
371
		return a->samples[i].percent < b->samples[i].percent;
372 373 374 375 376 377
	}
	return 0;
}

static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl,
				   int nr_events)
378
{
379
	struct rb_node **p = &root->rb_node;
380
	struct rb_node *parent = NULL;
381
	struct browser_disasm_line *l;
382 383 384

	while (*p != NULL) {
		parent = *p;
385
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
386 387

		if (disasm__cmp(bdl, l, nr_events))
388 389 390 391
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
392 393
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
394 395
}

396
static void annotate_browser__set_top(struct annotate_browser *browser,
397
				      struct disasm_line *pos, u32 idx)
398 399 400
{
	unsigned back;

401 402 403
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
404

405
	while (browser->b.top_idx != 0 && back != 0) {
406
		pos = list_entry(pos->node.prev, struct disasm_line, node);
407

408
		if (disasm_line__filter(&browser->b, &pos->node))
409 410
			continue;

411
		--browser->b.top_idx;
412 413 414
		--back;
	}

415 416
	browser->b.top = pos;
	browser->b.navkeypressed = true;
417 418 419 420 421
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
422
	struct browser_disasm_line *bpos;
423
	struct disasm_line *pos;
424
	u32 idx;
425

426 427
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
428
	idx = bpos->idx;
429
	if (annotate_browser__opts.hide_src_code)
430 431
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
432
	browser->curr_hot = nd;
433 434
}

435
static void annotate_browser__calc_percent(struct annotate_browser *browser,
436
					   struct perf_evsel *evsel)
437
{
438 439
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
440
	struct annotation *notes = symbol__annotation(sym);
441 442
	struct disasm_line *pos, *next;
	s64 len = symbol__size(sym);
443 444 445 446 447 448

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
449
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
450
		const char *path = NULL;
451 452
		double max_percent = 0.0;
		int i;
453 454 455 456 457 458 459 460

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

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

461
		for (i = 0; i < browser->nr_events; i++) {
462
			struct sym_hist_entry sample;
463 464

			bpos->samples[i].percent = disasm__calc_percent(notes,
465 466 467
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
468
						&path, &sample);
469
			bpos->samples[i].he = sample;
470

471 472
			if (max_percent < bpos->samples[i].percent)
				max_percent = bpos->samples[i].percent;
473 474
		}

475
		if (max_percent < 0.01 && pos->ipc == 0) {
476
			RB_CLEAR_NODE(&bpos->rb_node);
477 478
			continue;
		}
479 480
		disasm_rb_tree__insert(&browser->entries, bpos,
				       browser->nr_events);
481 482 483 484 485 486
	}
	pthread_mutex_unlock(&notes->lock);

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

487 488
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
489
	struct disasm_line *dl;
490
	struct browser_disasm_line *bdl;
491 492 493
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
494
	dl = list_entry(browser->b.top, struct disasm_line, node);
495
	bdl = disasm_line__browser(dl);
496

497
	if (annotate_browser__opts.hide_src_code) {
498 499
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
500 501

		browser->b.nr_entries = browser->nr_entries;
502
		annotate_browser__opts.hide_src_code = false;
503
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
504 505
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
506
	} else {
507
		if (bdl->idx_asm < 0) {
508 509 510 511 512
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

513 514
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
515 516

		browser->b.nr_entries = browser->nr_asm_entries;
517
		annotate_browser__opts.hide_src_code = true;
518
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
519 520
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
521 522 523 524 525
	}

	return true;
}

526 527 528 529 530 531
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;
}

532 533 534 535 536 537 538 539
#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);
}

540 541
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
542
				    struct hist_browser_timer *hbt)
543 544
{
	struct map_symbol *ms = browser->b.priv;
545
	struct disasm_line *dl = browser->selection;
546
	struct annotation *notes;
547 548
	struct addr_map_symbol target = {
		.map = ms->map,
549
		.addr = map__objdump_2mem(ms->map, dl->ops.target.addr),
550
	};
551
	char title[SYM_TITLE_MAX_SIZE];
552

553
	if (!ins__is_call(&dl->ins))
554 555
		return false;

556
	if (map_groups__find_ams(&target) ||
557 558 559
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
560 561 562 563
		ui_helpline__puts("The called function was not found.");
		return true;
	}

564
	notes = symbol__annotation(target.sym);
565 566
	pthread_mutex_lock(&notes->lock);

567
	if (notes->src == NULL && symbol__alloc_hist(target.sym) < 0) {
568 569
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
570
			    target.sym->name);
571 572 573 574
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
575 576
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
577
	ui_browser__show_title(&browser->b, title);
578 579 580
	return true;
}

581 582 583
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
584 585 586 587
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
588
	struct disasm_line *pos;
589 590 591 592 593

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
594
		if (!disasm_line__filter(&browser->b, &pos->node))
595 596 597 598 599 600 601 602
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
603
	struct disasm_line *dl = browser->selection;
604
	u64 offset;
605
	s64 idx;
606

607
	if (!ins__is_jump(&dl->ins))
608 609
		return false;

610 611
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
612
	if (dl == NULL) {
613
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
614 615 616
		return true;
	}

617
	annotate_browser__set_top(browser, dl, idx);
618

619 620 621
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
647
	struct disasm_line *dl;
648 649
	s64 idx;

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

656
	annotate_browser__set_top(browser, dl, idx);
657 658 659 660
	browser->searching_backwards = false;
	return true;
}

661 662 663
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
664 665 666 667
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
668
	struct disasm_line *pos = browser->selection;
669 670 671

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
672
		if (disasm_line__filter(&browser->b, &pos->node))
673 674 675 676 677 678 679 680 681 682 683 684 685
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
686
	struct disasm_line *dl;
687 688
	s64 idx;

689 690
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
691 692 693 694
		ui_helpline__puts("String not found!");
		return false;
	}

695
	annotate_browser__set_top(browser, dl, idx);
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 738 739 740 741 742 743 744 745 746 747
	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);
}

748 749 750 751 752 753 754 755 756 757 758 759 760
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;
}

761 762
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
763
				 struct hist_browser_timer *hbt)
764 765
{
	struct rb_node *nd = NULL;
766
	struct map_symbol *ms = browser->b.priv;
767
	struct symbol *sym = ms->sym;
768
	const char *help = "Press 'h' for help on key bindings";
769
	int delay_secs = hbt ? hbt->refresh : 0;
770
	int key;
771
	char title[SYM_TITLE_MAX_SIZE];
772

773 774
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
775
		return -1;
776

777
	annotate_browser__calc_percent(browser, evsel);
778

779 780 781
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
782
	}
783

784
	nd = browser->curr_hot;
785

786
	while (1) {
787
		key = ui_browser__run(&browser->b, delay_secs);
788

789
		if (delay_secs != 0) {
790
			annotate_browser__calc_percent(browser, evsel);
791 792 793 794 795 796 797 798 799
			/*
			 * 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;
		}

800
		switch (key) {
801
		case K_TIMER:
802 803
			if (hbt)
				hbt->timer(hbt->arg);
804 805

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

		if (nd != NULL)
935
			annotate_browser__set_rb_top(browser, nd);
936 937
	}
out:
938
	ui_browser__hide(&browser->b);
939
	return key;
940 941
}

942 943 944
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
945
	/* Set default value for show_total_period and show_nr_samples  */
946
	annotate_browser__opts.show_total_period =
947 948 949
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
950

951 952 953
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

954
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
955
			     struct hist_browser_timer *hbt)
956
{
957 958 959 960
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

961
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
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 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030 1031 1032

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

1033 1034 1035 1036
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
1037 1038 1039 1040 1041 1042
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
1043 1044 1045 1046 1047

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

1048
		if (!disasm_line__is_valid_jump(dl, sym))
1049 1050
			continue;

1051
		dlt = browser->offsets[dl->ops.target.offset];
1052 1053 1054 1055 1056 1057 1058
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

1059
		bdlt = disasm_line__browser(dlt);
1060 1061 1062 1063
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
1064 1065 1066
	}
}

1067 1068 1069 1070 1071 1072 1073 1074 1075
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1076 1077
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1078
			 struct hist_browser_timer *hbt)
1079
{
1080
	struct disasm_line *pos, *n;
1081
	struct annotation *notes;
1082
	size_t size;
1083 1084 1085 1086
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1087 1088
	struct annotate_browser browser = {
		.b = {
1089
			.refresh = annotate_browser__refresh,
1090 1091
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1092
			.filter  = disasm_line__filter,
1093
			.priv	 = &ms,
1094
			.use_navkeypressed = true,
1095
		},
1096
	};
1097
	int ret = -1, err;
1098 1099
	int nr_pcnt = 1;
	size_t sizeof_bdl = sizeof(struct browser_disasm_line);
1100

1101
	if (sym == NULL)
1102 1103
		return -1;

1104 1105
	size = symbol__size(sym);

1106
	if (map->dso->annotate_warned)
1107 1108
		return -1;

1109 1110 1111 1112 1113 1114
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

1115 1116
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
1117 1118
		sizeof_bdl += sizeof(struct disasm_line_samples) *
		  (nr_pcnt - 1);
1119 1120
	}

1121
	err = symbol__disassemble(sym, map, perf_evsel__env_arch(evsel),
1122 1123
				  sizeof_bdl, &browser.arch,
				  perf_evsel__env_cpuid(evsel));
1124 1125 1126 1127
	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);
1128
		goto out_free_offsets;
1129 1130
	}

1131
	ui_helpline__push("Press ESC to exit");
1132

1133
	notes = symbol__annotation(sym);
1134
	browser.start = map__rip_2objdump(map, sym->start);
1135

1136
	list_for_each_entry(pos, &notes->src->source, node) {
1137
		struct browser_disasm_line *bpos;
1138
		size_t line_len = strlen(pos->line);
1139

1140 1141
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1142 1143
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
1144
		if (pos->offset != -1) {
1145
			bpos->idx_asm = browser.nr_asm_entries++;
1146 1147 1148 1149 1150 1151 1152 1153 1154
			/*
			 * 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;
1155
		} else
1156
			bpos->idx_asm = -1;
1157 1158
	}

1159
	annotate_browser__mark_jump_targets(&browser, size);
1160
	annotate__compute_ipc(&browser, size, sym);
1161

1162
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1163
	browser.max_addr_width = hex_width(sym->end);
1164
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1165
	browser.nr_events = nr_pcnt;
1166
	browser.b.nr_entries = browser.nr_entries;
1167
	browser.b.entries = &notes->src->source,
1168
	browser.b.width += 18; /* Percentage */
1169 1170 1171 1172 1173 1174

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

	annotate_browser__update_addr_width(&browser);

1175
	ret = annotate_browser__run(&browser, evsel, hbt);
1176
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
1177
		list_del(&pos->node);
1178
		disasm_line__free(pos);
1179
	}
1180 1181 1182

out_free_offsets:
	free(browser.offsets);
1183 1184
	return ret;
}
1185 1186 1187

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

1189 1190 1191
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1192
static struct annotate_config {
1193 1194 1195 1196 1197
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1198
	ANNOTATE_CFG(show_linenr),
1199
	ANNOTATE_CFG(show_nr_jumps),
1200
	ANNOTATE_CFG(show_nr_samples),
1201
	ANNOTATE_CFG(show_total_period),
1202
	ANNOTATE_CFG(use_offset),
1203 1204 1205 1206 1207 1208
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1209
	const struct annotate_config *cfg = cfgp;
1210 1211 1212 1213

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

1214 1215
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1216
{
1217
	struct annotate_config *cfg;
1218 1219
	const char *name;

1220
	if (!strstarts(var, "annotate."))
1221 1222 1223 1224
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1225
		      sizeof(struct annotate_config), annotate_config__cmp);
1226 1227

	if (cfg == NULL)
1228 1229 1230
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1231 1232 1233 1234 1235 1236 1237
	return 0;
}

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