annotate.c 31.7 KB
Newer Older
1
// SPDX-License-Identifier: GPL-2.0
2
#include "../../util/util.h"
3 4
#include "../browser.h"
#include "../helpline.h"
5 6
#include "../ui.h"
#include "../util.h"
7 8 9 10
#include "../../util/annotate.h"
#include "../../util/hist.h"
#include "../../util/sort.h"
#include "../../util/symbol.h"
11
#include "../../util/evsel.h"
12
#include "../../util/config.h"
13
#include "../../util/evlist.h"
14
#include <inttypes.h>
15
#include <pthread.h>
16
#include <linux/kernel.h>
17
#include <linux/string.h>
18
#include <sys/ttydefaults.h>
19

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

25 26 27
#define IPC_WIDTH 6
#define CYCLES_WIDTH 6

28 29 30 31
struct browser_line {
	u32	idx;
	int	idx_asm;
	int	jump_sources;
32 33
};

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

47 48
struct arch;

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

72
static inline struct browser_line *browser_line(struct disasm_line *dl)
73
{
74
	return (void *) dl - sizeof(struct browser_line);
75 76
}

77 78
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
79
{
80
	if (annotate_browser__opts.hide_src_code) {
81 82 83
		struct annotation_line *al = list_entry(entry, struct annotation_line, node);

		return al->offset == -1;
84 85 86 87 88
	}

	return false;
}

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

108 109
static int annotate_browser__pcnt_width(struct annotate_browser *ab)
{
110
	return (annotate_browser__opts.show_total_period ? 12 : 7) * ab->nr_events;
111
}
112

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

118
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
119
{
120
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
121
	struct disasm_line *dl = list_entry(entry, struct disasm_line, al.node);
122
	struct browser_line *bdl = browser_line(dl);
123
	bool current_entry = ui_browser__is_current_entry(browser, row);
124
	bool change_color = (!annotate_browser__opts.hide_src_code &&
125 126 127
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
128 129
	int i, pcnt_width = annotate_browser__pcnt_width(ab),
	       cycles_width = annotate_browser__cycles_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 (dl->al.samples[i].percent > percent_max)
			percent_max = dl->al.samples[i].percent;
137 138
	}

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

147
	if (dl->al.offset != -1 && percent_max != 0.0) {
148 149
		for (i = 0; i < ab->nr_events; i++) {
			ui_browser__set_percent_color(browser,
150
						dl->al.samples[i].percent,
151 152
						current_entry);
			if (annotate_browser__opts.show_total_period) {
153
				ui_browser__printf(browser, "%11" PRIu64 " ",
154
						   dl->al.samples[i].he.period);
155 156
			} else if (annotate_browser__opts.show_nr_samples) {
				ui_browser__printf(browser, "%6" PRIu64 " ",
157
						   dl->al.samples[i].he.nr_samples);
158 159
			} else {
				ui_browser__printf(browser, "%6.2f ",
160
						   dl->al.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", pcnt_width,
170 171
					   annotate_browser__opts.show_total_period ? "Period" :
					   annotate_browser__opts.show_nr_samples ? "Samples" : "Percent");
172
		}
173 174
	}
	if (ab->have_cycles) {
175 176
		if (dl->al.ipc)
			ui_browser__printf(browser, "%*.2f ", IPC_WIDTH - 1, dl->al.ipc);
177
		else if (!show_title)
178
			ui_browser__write_nstring(browser, " ", IPC_WIDTH);
179 180 181
		else
			ui_browser__printf(browser, "%*s ", IPC_WIDTH - 1, "IPC");

182
		if (dl->al.cycles)
183
			ui_browser__printf(browser, "%*" PRIu64 " ",
184
					   CYCLES_WIDTH - 1, dl->al.cycles);
185
		else if (!show_title)
186
			ui_browser__write_nstring(browser, " ", CYCLES_WIDTH);
187 188
		else
			ui_browser__printf(browser, "%*s ", CYCLES_WIDTH - 1, "Cycle");
189 190
	}

191
	SLsmg_write_char(' ');
192 193

	/* The scroll bar isn't being used */
194
	if (!browser->navkeypressed)
195 196
		width += 1;

197
	if (!*dl->al.line)
198
		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
199 200
	else if (dl->al.offset == -1) {
		if (dl->al.line_nr && annotate_browser__opts.show_linenr)
201
			printed = scnprintf(bf, sizeof(bf), "%-*d ",
202
					ab->addr_width + 1, dl->al.line_nr);
203 204
		else
			printed = scnprintf(bf, sizeof(bf), "%*s  ",
205
				    ab->addr_width, " ");
206
		ui_browser__write_nstring(browser, bf, printed);
207
		ui_browser__write_nstring(browser, dl->al.line, width - printed - pcnt_width - cycles_width + 1);
208
	} else {
209
		u64 addr = dl->al.offset;
210
		int color = -1;
211

212
		if (!annotate_browser__opts.use_offset)
213 214
			addr += ab->start;

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

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

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

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

263
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
264
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
265
	}
266

267
	if (current_entry)
268
		ab->selection = &dl->al;
269 270
}

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

	return true;
}

282 283
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
284
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300
	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);
}

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

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

316
	if (!disasm_line__is_valid_jump(cursor, sym))
317
		return;
318

319 320 321
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
322

323 324
	bcursor = browser_line(cursor);
	btarget = browser_line(target);
325

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

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

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

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
348
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
349
	int ret = ui_browser__list_head_refresh(browser);
350
	int pcnt_width = annotate_browser__pcnt_width(ab);
351

352
	if (annotate_browser__opts.jump_arrows)
353
		annotate_browser__draw_current_jump(browser);
354

355
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
356
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
357 358 359
	return ret;
}

360
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
361 362 363
{
	int i;

364
	for (i = 0; i < a->samples_nr; i++) {
365
		if (a->samples[i].percent == b->samples[i].percent)
366
			continue;
367
		return a->samples[i].percent < b->samples[i].percent;
368 369 370 371
	}
	return 0;
}

372
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
373
{
374
	struct rb_node **p = &root->rb_node;
375
	struct rb_node *parent = NULL;
376
	struct annotation_line *l;
377 378 379

	while (*p != NULL) {
		parent = *p;
380
		l = rb_entry(parent, struct annotation_line, rb_node);
381

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

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

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

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

403
		if (disasm_line__filter(&browser->b, &pos->al.node))
404 405
			continue;

406
		--browser->b.top_idx;
407 408 409
		--back;
	}

410
	browser->b.top = &pos->al;
411
	browser->b.navkeypressed = true;
412 413 414 415 416
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
417
	struct browser_line *bpos;
418
	struct disasm_line *pos;
419
	u32 idx;
420

421
	pos = rb_entry(nd, struct disasm_line, al.rb_node);
422
	bpos = browser_line(pos);
423

424
	idx = bpos->idx;
425
	if (annotate_browser__opts.hide_src_code)
426 427
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
428
	browser->curr_hot = nd;
429 430
}

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

443 444
	symbol__calc_percent(sym, evsel);

445
	list_for_each_entry(pos, &notes->src->source, al.node) {
446 447
		double max_percent = 0.0;
		int i;
448

449
		if (pos->al.offset == -1) {
450
			RB_CLEAR_NODE(&pos->al.rb_node);
451 452 453
			continue;
		}

454
		for (i = 0; i < pos->al.samples_nr; i++) {
455 456
			struct annotation_data *sample = &pos->al.samples[i];

457 458
			if (max_percent < sample->percent)
				max_percent = sample->percent;
459 460
		}

461
		if (max_percent < 0.01 && pos->al.ipc == 0) {
462
			RB_CLEAR_NODE(&pos->al.rb_node);
463 464
			continue;
		}
465
		disasm_rb_tree__insert(&browser->entries, &pos->al);
466 467 468 469 470 471
	}
	pthread_mutex_unlock(&notes->lock);

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

472 473
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
474
	struct disasm_line *dl;
475
	struct browser_line *bdl;
476 477 478
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
479
	dl = list_entry(browser->b.top, struct disasm_line, al.node);
480
	bdl = browser_line(dl);
481

482
	if (annotate_browser__opts.hide_src_code) {
483 484
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
485 486

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

498 499
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
500 501

		browser->b.nr_entries = browser->nr_asm_entries;
502
		annotate_browser__opts.hide_src_code = true;
503
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
504 505
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
506 507 508 509 510
	}

	return true;
}

511 512 513 514 515 516
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;
}

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

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

538
	if (!ins__is_call(&dl->ins))
539 540
		return false;

541
	if (map_groups__find_ams(&target) ||
542 543 544
	    map__rip_2objdump(target.map, target.map->map_ip(target.map,
							     target.addr)) !=
	    dl->ops.target.addr) {
545 546 547 548
		ui_helpline__puts("The called function was not found.");
		return true;
	}

549
	notes = symbol__annotation(target.sym);
550 551
	pthread_mutex_lock(&notes->lock);

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

	pthread_mutex_unlock(&notes->lock);
560 561
	symbol__tui_annotate(target.sym, target.map, evsel, hbt);
	sym_title(ms->sym, ms->map, title, sizeof(title));
562
	ui_browser__show_title(&browser->b, title);
563 564 565
	return true;
}

566 567 568
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
569 570 571 572
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
573
	struct disasm_line *pos;
574 575

	*idx = 0;
576
	list_for_each_entry(pos, &notes->src->source, al.node) {
577
		if (pos->al.offset == offset)
578
			return pos;
579
		if (!disasm_line__filter(&browser->b, &pos->al.node))
580 581 582 583 584 585 586 587
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
588
	struct disasm_line *dl = disasm_line(browser->selection);
589
	u64 offset;
590
	s64 idx;
591

592
	if (!ins__is_jump(&dl->ins))
593 594
		return false;

595 596
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
597
	if (dl == NULL) {
598
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
599 600 601
		return true;
	}

602
	annotate_browser__set_top(browser, dl, idx);
603

604 605 606
	return true;
}

607 608 609
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
610 611 612 613
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
614
	struct disasm_line *pos = disasm_line(browser->selection);
615 616

	*idx = browser->b.index;
617 618
	list_for_each_entry_continue(pos, &notes->src->source, al.node) {
		if (disasm_line__filter(&browser->b, &pos->al.node))
619 620 621 622
			continue;

		++*idx;

623
		if (pos->al.line && strstr(pos->al.line, s) != NULL)
624 625 626 627 628 629 630 631
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
632
	struct disasm_line *dl;
633 634
	s64 idx;

635 636
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
637 638 639 640
		ui_helpline__puts("String not found!");
		return false;
	}

641
	annotate_browser__set_top(browser, dl, idx);
642 643 644 645
	browser->searching_backwards = false;
	return true;
}

646 647 648
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
649 650 651 652
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
653
	struct disasm_line *pos = disasm_line(browser->selection);
654 655

	*idx = browser->b.index;
656 657
	list_for_each_entry_continue_reverse(pos, &notes->src->source, al.node) {
		if (disasm_line__filter(&browser->b, &pos->al.node))
658 659 660 661
			continue;

		--*idx;

662
		if (pos->al.line && strstr(pos->al.line, s) != NULL)
663 664 665 666 667 668 669 670
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
671
	struct disasm_line *dl;
672 673
	s64 idx;

674 675
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
676 677 678 679
		ui_helpline__puts("String not found!");
		return false;
	}

680
	annotate_browser__set_top(browser, dl, idx);
681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732
	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);
}

733 734 735 736 737 738 739 740 741 742 743 744 745
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;
}

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

758 759
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
760
		return -1;
761

762
	annotate_browser__calc_percent(browser, evsel);
763

764 765 766
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
767
	}
768

769
	nd = browser->curr_hot;
770

771
	while (1) {
772
		key = ui_browser__run(&browser->b, delay_secs);
773

774
		if (delay_secs != 0) {
775
			annotate_browser__calc_percent(browser, evsel);
776 777 778 779 780 781 782 783 784
			/*
			 * 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;
		}

785
		switch (key) {
786
		case K_TIMER:
787 788
			if (hbt)
				hbt->timer(hbt->arg);
789 790

			if (delay_secs != 0)
791
				symbol__annotate_decay_histogram(sym, evsel->idx);
792
			continue;
793
		case K_TAB:
794 795 796
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
797
					nd = rb_last(&browser->entries);
798
			} else
799
				nd = browser->curr_hot;
800
			break;
801
		case K_UNTAB:
802
			if (nd != NULL) {
803 804
				nd = rb_next(nd);
				if (nd == NULL)
805
					nd = rb_first(&browser->entries);
806
			} else
807
				nd = browser->curr_hot;
808
			break;
809
		case K_F1:
810
		case 'h':
811
			ui_browser__help_window(&browser->b,
812 813 814
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
815 816
		"ENTER         Go to target\n"
		"ESC           Exit\n"
817 818
		"H             Go to hottest instruction\n"
		"TAB/shift+TAB Cycle thru hottest instructions\n"
819 820 821 822 823
		"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"
824
		"t             Circulate percent, total period, samples view\n"
825
		"/             Search string\n"
826
		"k             Toggle line numbers\n"
827
		"r             Run available scripts\n"
828
		"?             Search string backwards\n");
829
			continue;
830 831 832 833 834
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
835 836 837 838
		case 'k':
			annotate_browser__opts.show_linenr =
				!annotate_browser__opts.show_linenr;
			break;
839
		case 'H':
840
			nd = browser->curr_hot;
841
			break;
842
		case 's':
843
			if (annotate_browser__toggle_source(browser))
844 845
				ui_helpline__puts(help);
			continue;
846
		case 'o':
847
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
848
			annotate_browser__update_addr_width(browser);
849
			continue;
850
		case 'j':
851
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
852
			continue;
853
		case 'J':
854
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
855
			annotate_browser__update_addr_width(browser);
856
			continue;
857
		case '/':
858
			if (annotate_browser__search(browser, delay_secs)) {
859 860 861 862 863
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
864 865 866
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
867 868 869
				goto show_help;
			continue;
		case '?':
870
			if (annotate_browser__search_reverse(browser, delay_secs))
871 872
				goto show_help;
			continue;
873 874 875 876
		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",
877 878 879 880 881
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
882 883
		}
			continue;
884 885
		case K_ENTER:
		case K_RIGHT:
886 887 888
		{
			struct disasm_line *dl = disasm_line(browser->selection);

889
			if (browser->selection == NULL)
890
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
891
			else if (browser->selection->offset == -1)
892
				ui_helpline__puts("Actions are only available for assembly lines.");
893
			else if (!dl->ins.ops)
894
				goto show_sup_ins;
895
			else if (ins__is_ret(&dl->ins))
896
				goto out;
897
			else if (!(annotate_browser__jump(browser) ||
898
				     annotate_browser__callq(browser, evsel, hbt))) {
899
show_sup_ins:
900
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
901
			}
902
			continue;
903
		}
904
		case 't':
905 906 907 908 909 910 911
			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;
912 913
			annotate_browser__update_addr_width(browser);
			continue;
914 915
		case K_LEFT:
		case K_ESC:
916 917
		case 'q':
		case CTRL('c'):
918
			goto out;
919 920
		default:
			continue;
921
		}
922 923

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

931 932 933
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
934
	/* Set default value for show_total_period and show_nr_samples  */
935
	annotate_browser__opts.show_total_period =
936 937 938
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
939

940 941 942
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

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

950
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
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

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)
984
				dl->al.ipc = ipc;
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
		}
	}
}

/*
 * 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)
1015
				dl->al.cycles = ch->cycles_aggr / ch->num_aggr;
1016 1017 1018 1019 1020 1021
			browser->have_cycles = true;
		}
	}
	pthread_mutex_unlock(&notes->lock);
}

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

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

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

1037
		if (!disasm_line__is_valid_jump(dl, sym))
1038 1039
			continue;

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

1048
		bdlt = browser_line(dlt);
1049 1050 1051 1052
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
1053 1054 1055
	}
}

1056 1057 1058 1059 1060 1061 1062 1063 1064
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

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

1089
	if (sym == NULL)
1090 1091
		return -1;

1092 1093
	size = symbol__size(sym);

1094
	if (map->dso->annotate_warned)
1095 1096
		return -1;

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

1103
	if (perf_evsel__is_group_event(evsel))
1104 1105
		nr_pcnt = evsel->nr_members;

1106
	err = symbol__annotate(sym, map, evsel,
1107
			       sizeof(struct browser_line), &browser.arch,
1108
			       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, al.node) {
1122
		struct browser_line *bpos;
1123
		size_t line_len = strlen(pos->al.line);
1124

1125 1126
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1127
		bpos = browser_line(pos);
1128
		bpos->idx = browser.nr_entries++;
1129
		if (pos->al.offset != -1) {
1130
			bpos->idx_asm = browser.nr_asm_entries++;
1131 1132 1133 1134 1135 1136 1137
			/*
			 * 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
 			 */
1138 1139
			if (pos->al.offset < (s64)size)
				browser.offsets[pos->al.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 1162

	annotated_source__purge(notes->src);
1163 1164 1165

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

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

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

#undef ANNOTATE_CFG

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

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

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

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

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

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

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