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

72
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
73
{
74
	return (void *) dl - sizeof(struct browser_disasm_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_disasm_line *bdl = disasm_line__browser(dl);
123
	bool current_entry = ui_browser__is_current_entry(browser, row);
124
	bool change_color = (!annotate_browser__opts.hide_src_code &&
125 126 127
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
128 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;
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 = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
306
	unsigned int from, to;
307 308
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
309
	u8 pcnt_width = annotate_browser__pcnt_width(ab);
310 311 312 313

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

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

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

322 323
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
324

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

442 443
	symbol__calc_percent(sym, evsel);

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

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

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

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

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

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

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

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

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

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

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

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

	return true;
}

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

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

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

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

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

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

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

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

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

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

	return NULL;
}

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

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

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

601
	annotate_browser__set_top(browser, dl, idx);
602

603 604 605
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

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

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

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

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

761
	annotate_browser__calc_percent(browser, evsel);
762

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

768
	nd = browser->curr_hot;
769

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

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

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

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

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

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

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

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

945
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
946 947
}

948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978

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

/*
 * 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)
1010
				dl->al.cycles = ch->cycles_aggr / ch->num_aggr;
1011 1012 1013 1014 1015 1016
			browser->have_cycles = true;
		}
	}
	pthread_mutex_unlock(&notes->lock);
}

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

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

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

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

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

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

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

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

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

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

1087 1088
	size = symbol__size(sym);

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

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

1098
	if (perf_evsel__is_group_event(evsel))
1099 1100
		nr_pcnt = evsel->nr_members;

1101
	err = symbol__annotate(sym, map, evsel,
1102
			       sizeof(struct browser_disasm_line), &browser.arch,
1103
			       perf_evsel__env_cpuid(evsel));
1104 1105 1106 1107
	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);
1108
		goto out_free_offsets;
1109 1110
	}

1111
	ui_helpline__push("Press ESC to exit");
1112

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

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

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

1139
	annotate_browser__mark_jump_targets(&browser, size);
1140
	annotate__compute_ipc(&browser, size, sym);
1141

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

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

	annotate_browser__update_addr_width(&browser);

1155
	ret = annotate_browser__run(&browser, evsel, hbt);
1156 1157

	annotated_source__purge(notes->src);
1158 1159 1160

out_free_offsets:
	free(browser.offsets);
1161 1162
	return ret;
}
1163 1164 1165

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

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

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1187
	const struct annotate_config *cfg = cfgp;
1188 1189 1190 1191

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

1192 1193
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1194
{
1195
	struct annotate_config *cfg;
1196 1197
	const char *name;

1198
	if (!strstarts(var, "annotate."))
1199 1200 1201 1202
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1203
		      sizeof(struct annotate_config), annotate_config__cmp);
1204 1205

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

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