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 360
static int disasm__cmp(struct annotation_line *a,
		       struct annotation_line *b, int nr_pcnt)
361 362 363 364
{
	int i;

	for (i = 0; i < nr_pcnt; 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
				   int nr_events)
374
{
375
	struct rb_node **p = &root->rb_node;
376
	struct rb_node *parent = NULL;
377
	struct annotation_line *l;
378 379 380

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

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

444 445
	symbol__calc_percent(sym, evsel);

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

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

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

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

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

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

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

	browser->b.seek(&browser->b, offset, SEEK_CUR);
481
	dl = list_entry(browser->b.top, struct disasm_line, al.node);
482
	bdl = disasm_line__browser(dl);
483

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

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

500 501
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
502 503

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

	return true;
}

513 514 515 516 517 518
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;
}

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

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

540
	if (!ins__is_call(&dl->ins))
541 542
		return false;

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

551
	notes = symbol__annotation(target.sym);
552 553
	pthread_mutex_lock(&notes->lock);

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

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

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

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

	return NULL;
}

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

594
	if (!ins__is_jump(&dl->ins))
595 596
		return false;

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

604
	annotate_browser__set_top(browser, dl, idx);
605

606 607 608
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

682
	annotate_browser__set_top(browser, dl, idx);
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 733 734
	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);
}

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

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

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

764
	annotate_browser__calc_percent(browser, evsel);
765

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

771
	nd = browser->curr_hot;
772

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

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

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

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

938 939 940
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

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

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

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

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

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

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

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

1035
		if (!disasm_line__is_valid_jump(dl, sym))
1036 1037
			continue;

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

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

		++browser->nr_jumps;
1051 1052 1053
	}
}

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

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

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

1090 1091
	size = symbol__size(sym);

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

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

1101
	if (perf_evsel__is_group_event(evsel))
1102 1103
		nr_pcnt = evsel->nr_members;

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

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

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

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

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

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

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

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

	annotate_browser__update_addr_width(&browser);

1158
	ret = annotate_browser__run(&browser, evsel, hbt);
1159 1160

	annotated_source__purge(notes->src);
1161 1162 1163

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

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

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

#undef ANNOTATE_CFG

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

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

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

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

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

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

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