annotate.c 31.5 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 45
	     show_nr_jumps,
	     show_total_period;
46 47 48 49 50
} annotate_browser__opts = {
	.use_offset	= true,
	.jump_arrows	= true,
};

51 52
struct arch;

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

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

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

	return false;
}

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

111 112
static int annotate_browser__pcnt_width(struct annotate_browser *ab)
{
113 114
	return 7 * ab->nr_events;
}
115

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

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

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

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

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

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

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

188
	SLsmg_write_char(' ');
189 190

	/* The scroll bar isn't being used */
191
	if (!browser->navkeypressed)
192 193
		width += 1;

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

209
		if (!annotate_browser__opts.use_offset)
210 211
			addr += ab->start;

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

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

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

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

260
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
261
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
262
	}
263

264
	if (current_entry)
265
		ab->selection = dl;
266 267
}

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

	return true;
}

279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
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);
}

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

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

312
	if (!disasm_line__is_valid_jump(cursor, sym))
313
		return;
314

315 316 317
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
318

319 320
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
321

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

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

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
344
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
345
	int ret = ui_browser__list_head_refresh(browser);
346
	int pcnt_width = annotate_browser__pcnt_width(ab);
347

348
	if (annotate_browser__opts.jump_arrows)
349
		annotate_browser__draw_current_jump(browser);
350

351
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
352
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
353 354 355
	return ret;
}

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

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

	while (*p != NULL) {
		parent = *p;
378
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
379 380

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

389
static void annotate_browser__set_top(struct annotate_browser *browser,
390
				      struct disasm_line *pos, u32 idx)
391 392 393
{
	unsigned back;

394 395 396
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
397

398
	while (browser->b.top_idx != 0 && back != 0) {
399
		pos = list_entry(pos->node.prev, struct disasm_line, node);
400

401
		if (disasm_line__filter(&browser->b, &pos->node))
402 403
			continue;

404
		--browser->b.top_idx;
405 406 407
		--back;
	}

408 409
	browser->b.top = pos;
	browser->b.navkeypressed = true;
410 411 412 413 414
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
415
	struct browser_disasm_line *bpos;
416
	struct disasm_line *pos;
417
	u32 idx;
418

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

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

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

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

454
		for (i = 0; i < browser->nr_events; i++) {
455
			struct sym_hist_entry sample;
456 457

			bpos->samples[i].percent = disasm__calc_percent(notes,
458 459 460
						evsel->idx + i,
						pos->offset,
						next ? next->offset : len,
461
						&path, &sample);
462
			bpos->samples[i].he = sample;
463

464 465
			if (max_percent < bpos->samples[i].percent)
				max_percent = bpos->samples[i].percent;
466 467
		}

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

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

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

	browser->b.seek(&browser->b, offset, SEEK_CUR);
487
	dl = list_entry(browser->b.top, struct disasm_line, node);
488
	bdl = disasm_line__browser(dl);
489

490
	if (annotate_browser__opts.hide_src_code) {
491 492
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
493 494

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

506 507
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
508 509

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

	return true;
}

519 520 521 522 523 524
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;
}

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

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

546
	if (!ins__is_call(&dl->ins))
547 548
		return false;

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

557
	notes = symbol__annotation(target.sym);
558 559
	pthread_mutex_lock(&notes->lock);

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

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

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

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

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
596
	struct disasm_line *dl = browser->selection;
597
	u64 offset;
598
	s64 idx;
599

600
	if (!ins__is_jump(&dl->ins))
601 602
		return false;

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

610
	annotate_browser__set_top(browser, dl, idx);
611

612 613 614
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
640
	struct disasm_line *dl;
641 642
	s64 idx;

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

649
	annotate_browser__set_top(browser, dl, idx);
650 651 652 653
	browser->searching_backwards = false;
	return true;
}

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

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

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
679
	struct disasm_line *dl;
680 681
	s64 idx;

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

688
	annotate_browser__set_top(browser, dl, idx);
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 738 739 740
	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);
}

741 742 743 744 745 746 747 748 749 750 751 752 753
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;
}

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

766 767
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
768
		return -1;
769

770
	annotate_browser__calc_percent(browser, evsel);
771

772 773 774
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
775
	}
776

777
	nd = browser->curr_hot;
778

779
	while (1) {
780
		key = ui_browser__run(&browser->b, delay_secs);
781

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

793
		switch (key) {
794
		case K_TIMER:
795 796
			if (hbt)
				hbt->timer(hbt->arg);
797 798

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

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

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

936 937 938
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

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

946
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
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 1015 1016 1017

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

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

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
1028 1029 1030 1031 1032

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

1033
		if (!disasm_line__is_valid_jump(dl, sym))
1034 1035
			continue;

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

1044
		bdlt = disasm_line__browser(dlt);
1045 1046 1047 1048
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
1049 1050 1051
	}
}

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

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

1086
	if (sym == NULL)
1087 1088
		return -1;

1089 1090
	size = symbol__size(sym);

1091
	if (map->dso->annotate_warned)
1092 1093
		return -1;

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

1100 1101
	if (perf_evsel__is_group_event(evsel)) {
		nr_pcnt = evsel->nr_members;
1102 1103
		sizeof_bdl += sizeof(struct disasm_line_samples) *
		  (nr_pcnt - 1);
1104 1105
	}

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

1116
	ui_helpline__push("Press ESC to exit");
1117

1118
	notes = symbol__annotation(sym);
1119
	browser.start = map__rip_2objdump(map, sym->start);
1120

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

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

1144
	annotate_browser__mark_jump_targets(&browser, size);
1145
	annotate__compute_ipc(&browser, size, sym);
1146

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

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

	annotate_browser__update_addr_width(&browser);

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

out_free_offsets:
	free(browser.offsets);
1168 1169
	return ret;
}
1170 1171 1172

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

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

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1193
	const struct annotate_config *cfg = cfgp;
1194 1195 1196 1197

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

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

1204
	if (!strstarts(var, "annotate."))
1205 1206 1207 1208
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1209
		      sizeof(struct annotate_config), annotate_config__cmp);
1210 1211

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

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