annotate.c 30.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 28
struct browser_line {
	u32	idx;
	int	idx_asm;
	int	jump_sources;
29 30
};

31
static struct annotation_options annotate_browser__opts = {
32 33 34 35
	.use_offset	= true,
	.jump_arrows	= true,
};

36 37
struct arch;

38
struct annotate_browser {
39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
	struct ui_browser	    b;
	struct rb_root		    entries;
	struct rb_node		   *curr_hot;
	struct annotation_line	   *selection;
	struct arch		   *arch;
	u64			    start;
	int			    nr_asm_entries;
	int			    nr_entries;
	int			    max_jump_sources;
	int			    nr_jumps;
	bool			    searching_backwards;
	u8			    addr_width;
	u8			    jumps_width;
	u8			    target_width;
	u8			    min_addr_width;
	u8			    max_addr_width;
	char			    search_bf[128];
56 57
};

58 59 60 61 62 63
static inline struct annotation *browser__annotation(struct ui_browser *browser)
{
	struct map_symbol *ms = browser->priv;
	return symbol__annotation(ms->sym);
}

64
static inline struct browser_line *browser_line(struct annotation_line *al)
65
{
66 67 68 69
	void *ptr = al;

	ptr = container_of(al, struct disasm_line, al);
	return ptr - sizeof(struct browser_line);
70 71
}

72
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
73
{
74
	struct annotation *notes = browser__annotation(browser);
75 76

	if (notes->options->hide_src_code) {
77 78 79
		struct annotation_line *al = list_entry(entry, struct annotation_line, node);

		return al->offset == -1;
80 81 82 83 84
	}

	return false;
}

85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
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);
}

104 105 106
static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
			       char *bf, size_t size)
{
107
	struct annotation *notes = browser__annotation(browser);
108

109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128
	if (dl->ins.ops && dl->ins.ops->scnprintf) {
		if (ins__is_jump(&dl->ins)) {
			bool fwd = dl->ops.target.offset > dl->al.offset;

			ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
							    SLSMG_UARROW_CHAR);
			SLsmg_write_char(' ');
		} else if (ins__is_call(&dl->ins)) {
			ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
			SLsmg_write_char(' ');
		} else if (ins__is_ret(&dl->ins)) {
			ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
			SLsmg_write_char(' ');
		} else {
			ui_browser__write_nstring(browser, " ", 2);
		}
	} else {
		ui_browser__write_nstring(browser, " ", 2);
	}

129
	disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset);
130 131
}

132
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
133
{
134
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
135
	struct annotation *notes = browser__annotation(browser);
136 137
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
	struct browser_line *bl = browser_line(al);
138
	bool current_entry = ui_browser__is_current_entry(browser, row);
139
	bool change_color = (!notes->options->hide_src_code &&
140 141 142
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
143
	int i, pcnt_width = annotation__pcnt_width(notes),
144
	       cycles_width = annotation__cycles_width(notes);
145
	double percent_max = 0.0;
146
	char bf[256];
147
	bool show_title = false;
148

149
	for (i = 0; i < notes->nr_events; i++) {
150 151
		if (al->samples[i].percent > percent_max)
			percent_max = al->samples[i].percent;
152 153
	}

154
	if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
155
		if (notes->have_cycles) {
156
			if (al->ipc == 0.0 && al->cycles == 0)
157 158 159 160 161
				show_title = true;
		} else
			show_title = true;
	}

162
	if (al->offset != -1 && percent_max != 0.0) {
163
		for (i = 0; i < notes->nr_events; i++) {
164
			ui_browser__set_percent_color(browser,
165
						al->samples[i].percent,
166
						current_entry);
167
			if (notes->options->show_total_period) {
168
				ui_browser__printf(browser, "%11" PRIu64 " ",
169
						   al->samples[i].he.period);
170
			} else if (notes->options->show_nr_samples) {
171
				ui_browser__printf(browser, "%6" PRIu64 " ",
172
						   al->samples[i].he.nr_samples);
173 174
			} else {
				ui_browser__printf(browser, "%6.2f ",
175
						   al->samples[i].percent);
176
			}
177
		}
178
	} else {
179
		ui_browser__set_percent_color(browser, 0, current_entry);
180 181

		if (!show_title)
182
			ui_browser__write_nstring(browser, " ", pcnt_width);
183 184
		else {
			ui_browser__printf(browser, "%*s", pcnt_width,
185 186
					   notes->options->show_total_period ? "Period" :
					   notes->options->show_nr_samples ? "Samples" : "Percent");
187
		}
188
	}
189
	if (notes->have_cycles) {
190
		if (al->ipc)
191
			ui_browser__printf(browser, "%*.2f ", ANNOTATION__IPC_WIDTH - 1, al->ipc);
192
		else if (!show_title)
193
			ui_browser__write_nstring(browser, " ", ANNOTATION__IPC_WIDTH);
194
		else
195
			ui_browser__printf(browser, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC");
196

197
		if (al->cycles)
198
			ui_browser__printf(browser, "%*" PRIu64 " ",
199
					   ANNOTATION__CYCLES_WIDTH - 1, al->cycles);
200
		else if (!show_title)
201
			ui_browser__write_nstring(browser, " ", ANNOTATION__CYCLES_WIDTH);
202
		else
203
			ui_browser__printf(browser, "%*s ", ANNOTATION__CYCLES_WIDTH - 1, "Cycle");
204 205
	}

206
	SLsmg_write_char(' ');
207 208

	/* The scroll bar isn't being used */
209
	if (!browser->navkeypressed)
210 211
		width += 1;

212
	if (!*al->line)
213
		ui_browser__write_nstring(browser, " ", width - pcnt_width - cycles_width);
214
	else if (al->offset == -1) {
215
		if (al->line_nr && notes->options->show_linenr)
216
			printed = scnprintf(bf, sizeof(bf), "%-*d ",
217
					ab->addr_width + 1, al->line_nr);
218 219
		else
			printed = scnprintf(bf, sizeof(bf), "%*s  ",
220
				    ab->addr_width, " ");
221
		ui_browser__write_nstring(browser, bf, printed);
222
		ui_browser__write_nstring(browser, al->line, width - printed - pcnt_width - cycles_width + 1);
223
	} else {
224
		u64 addr = al->offset;
225
		int color = -1;
226

227
		if (!notes->options->use_offset)
228 229
			addr += ab->start;

230
		if (!notes->options->use_offset) {
231
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
232
		} else {
233
			if (bl->jump_sources) {
234
				if (notes->options->show_nr_jumps) {
235 236 237
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
							    ab->jumps_width,
238 239
							    bl->jump_sources);
					prev = annotate_browser__set_jumps_percent_color(ab, bl->jump_sources,
240
											 current_entry);
241
					ui_browser__write_nstring(browser, bf, printed);
242
					ui_browser__set_color(browser, prev);
243 244
				}

245
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
246
						    ab->target_width, addr);
247
			} else {
248 249
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
250 251
			}
		}
252

253
		if (change_color)
254
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
255
		ui_browser__write_nstring(browser, bf, printed);
256
		if (change_color)
257
			ui_browser__set_color(browser, color);
258

259 260
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

261
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
262
	}
263

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

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

	return true;
}

279 280
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
281
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297
	const char *name;

	if (!pos)
		return false;

	if (ins__is_lock(&pos->ins))
		name = pos->ops.locked.ins.name;
	else
		name = pos->ins.name;

	if (!name || !cursor->ins.name)
		return false;

	return ins__is_fused(ab->arch, name, cursor->ins.name);
}

298
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
299 300
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
301
	struct disasm_line *cursor = disasm_line(ab->selection);
302
	struct annotation_line *target;
303
	struct browser_line *btarget, *bcursor;
304
	unsigned int from, to;
305 306
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
307
	struct annotation *notes = symbol__annotation(sym);
308
	u8 pcnt_width = annotation__pcnt_width(notes);
309
	int width;
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 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337
	/*
	 * This first was seen with a gcc function, _cpp_lex_token, that
	 * has the usual jumps:
	 *
	 *  │1159e6c: ↓ jne    115aa32 <_cpp_lex_token@@Base+0xf92>
	 *
	 * I.e. jumps to a label inside that function (_cpp_lex_token), and
	 * those works, but also this kind:
	 *
	 *  │1159e8b: ↓ jne    c469be <cpp_named_operator2name@@Base+0xa72>
	 *
	 *  I.e. jumps to another function, outside _cpp_lex_token, which
	 *  are not being correctly handled generating as a side effect references
	 *  to ab->offset[] entries that are set to NULL, so to make this code
	 *  more robust, check that here.
	 *
	 *  A proper fix for will be put in place, looking at the function
	 *  name right after the '<' token and probably treating this like a
	 *  'call' instruction.
	 */
338
	target = notes->offsets[cursor->ops.target.offset];
339
	if (target == NULL) {
340
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
341 342 343
				    cursor->ops.target.offset);
		return;
	}
344

345
	bcursor = browser_line(&cursor->al);
346
	btarget = browser_line(target);
347

348
	if (notes->options->hide_src_code) {
349
		from = bcursor->idx_asm;
350 351
		to = btarget->idx_asm;
	} else {
352
		from = (u64)bcursor->idx;
353 354 355
		to = (u64)btarget->idx;
	}

356
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
357

358
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
359 360
	__ui_browser__line_arrow(browser,
				 pcnt_width + 2 + ab->addr_width + width,
361
				 from, to);
362 363 364

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
J
Jin Yao 已提交
365
				       pcnt_width + 3 + ab->addr_width + width,
366 367 368
				       from - 1,
				       to > from ? true : false);
	}
369 370 371 372
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
373
	struct annotation *notes = browser__annotation(browser);
374
	int ret = ui_browser__list_head_refresh(browser);
375
	int pcnt_width = annotation__pcnt_width(notes);
376

377
	if (notes->options->jump_arrows)
378
		annotate_browser__draw_current_jump(browser);
379

380
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
381
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
382 383 384
	return ret;
}

385
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
386 387 388
{
	int i;

389
	for (i = 0; i < a->samples_nr; i++) {
390
		if (a->samples[i].percent == b->samples[i].percent)
391
			continue;
392
		return a->samples[i].percent < b->samples[i].percent;
393 394 395 396
	}
	return 0;
}

397
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
398
{
399
	struct rb_node **p = &root->rb_node;
400
	struct rb_node *parent = NULL;
401
	struct annotation_line *l;
402 403 404

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

407
		if (disasm__cmp(al, l))
408 409 410 411
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
412 413
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
414 415
}

416
static void annotate_browser__set_top(struct annotate_browser *browser,
417
				      struct annotation_line *pos, u32 idx)
418 419 420
{
	unsigned back;

421 422 423
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
424

425
	while (browser->b.top_idx != 0 && back != 0) {
426
		pos = list_entry(pos->node.prev, struct annotation_line, node);
427

428
		if (disasm_line__filter(&browser->b, &pos->node))
429 430
			continue;

431
		--browser->b.top_idx;
432 433 434
		--back;
	}

435
	browser->b.top = pos;
436
	browser->b.navkeypressed = true;
437 438 439 440 441
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
442
	struct annotation *notes = browser__annotation(&browser->b);
443
	struct browser_line *bpos;
444
	struct annotation_line *pos;
445
	u32 idx;
446

447 448
	pos = rb_entry(nd, struct annotation_line, rb_node);
	bpos = browser_line(pos);
449

450
	idx = bpos->idx;
451
	if (notes->options->hide_src_code)
452 453
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
454
	browser->curr_hot = nd;
455 456
}

457
static void annotate_browser__calc_percent(struct annotate_browser *browser,
458
					   struct perf_evsel *evsel)
459
{
460 461
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
462
	struct annotation *notes = symbol__annotation(sym);
463
	struct disasm_line *pos;
464 465 466 467 468

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

469 470
	symbol__calc_percent(sym, evsel);

471
	list_for_each_entry(pos, &notes->src->source, al.node) {
472 473
		double max_percent = 0.0;
		int i;
474

475
		if (pos->al.offset == -1) {
476
			RB_CLEAR_NODE(&pos->al.rb_node);
477 478 479
			continue;
		}

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

483 484
			if (max_percent < sample->percent)
				max_percent = sample->percent;
485 486
		}

487
		if (max_percent < 0.01 && pos->al.ipc == 0) {
488
			RB_CLEAR_NODE(&pos->al.rb_node);
489 490
			continue;
		}
491
		disasm_rb_tree__insert(&browser->entries, &pos->al);
492 493 494 495 496 497
	}
	pthread_mutex_unlock(&notes->lock);

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

498 499
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
500
	struct annotation *notes = browser__annotation(&browser->b);
501
	struct annotation_line *al;
502
	struct browser_line *bl;
503 504 505
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
506 507
	al = list_entry(browser->b.top, struct annotation_line, node);
	bl = browser_line(al);
508

509
	if (notes->options->hide_src_code) {
510 511
		if (bl->idx_asm < offset)
			offset = bl->idx;
512 513

		browser->b.nr_entries = browser->nr_entries;
514
		notes->options->hide_src_code = false;
515
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
516 517
		browser->b.top_idx = bl->idx - offset;
		browser->b.index = bl->idx;
518
	} else {
519
		if (bl->idx_asm < 0) {
520 521 522 523 524
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

525 526
		if (bl->idx_asm < offset)
			offset = bl->idx_asm;
527 528

		browser->b.nr_entries = browser->nr_asm_entries;
529
		notes->options->hide_src_code = true;
530
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
531 532
		browser->b.top_idx = bl->idx_asm - offset;
		browser->b.index = bl->idx_asm;
533 534 535 536 537
	}

	return true;
}

538 539 540 541 542 543
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;
}

544 545 546 547 548 549 550 551
#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);
}

552 553
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
554
				    struct hist_browser_timer *hbt)
555 556
{
	struct map_symbol *ms = browser->b.priv;
557
	struct disasm_line *dl = disasm_line(browser->selection);
558
	struct annotation *notes;
559
	char title[SYM_TITLE_MAX_SIZE];
560

561
	if (!ins__is_call(&dl->ins))
562 563
		return false;

564
	if (!dl->ops.target.sym) {
565 566 567 568
		ui_helpline__puts("The called function was not found.");
		return true;
	}

569
	notes = symbol__annotation(dl->ops.target.sym);
570 571
	pthread_mutex_lock(&notes->lock);

572
	if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
573 574
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
575
			    dl->ops.target.sym->name);
576 577 578 579
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
580
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
581
	sym_title(ms->sym, ms->map, title, sizeof(title));
582
	ui_browser__show_title(&browser->b, title);
583 584 585
	return true;
}

586 587 588
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
589
{
590
	struct annotation *notes = browser__annotation(&browser->b);
591
	struct disasm_line *pos;
592 593

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

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
606
	struct disasm_line *dl = disasm_line(browser->selection);
607
	u64 offset;
608
	s64 idx;
609

610
	if (!ins__is_jump(&dl->ins))
611 612
		return false;

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

620
	annotate_browser__set_top(browser, &dl->al, idx);
621

622 623 624
	return true;
}

625
static
626
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
627
					  char *s, s64 *idx)
628
{
629
	struct annotation *notes = browser__annotation(&browser->b);
630
	struct annotation_line *al = browser->selection;
631 632

	*idx = browser->b.index;
633 634
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
635 636 637 638
			continue;

		++*idx;

639 640
		if (al->line && strstr(al->line, s) != NULL)
			return al;
641 642 643 644 645 646 647
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
648
	struct annotation_line *al;
649 650
	s64 idx;

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

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

662
static
663
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
664
						  char *s, s64 *idx)
665
{
666
	struct annotation *notes = browser__annotation(&browser->b);
667
	struct annotation_line *al = browser->selection;
668 669

	*idx = browser->b.index;
670 671
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
672 673 674 675
			continue;

		--*idx;

676 677
		if (al->line && strstr(al->line, s) != NULL)
			return al;
678 679 680 681 682 683 684
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
685
	struct annotation_line *al;
686 687
	s64 idx;

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

694
	annotate_browser__set_top(browser, al, idx);
695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746
	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);
}

747 748
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
749
	struct annotation *notes = browser__annotation(&browser->b);
750 751

	if (notes->options->use_offset)
752 753 754 755 756 757
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

758
	if (notes->options->show_nr_jumps)
759 760 761
		browser->addr_width += browser->jumps_width + 1;
}

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

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

779
	annotate_browser__calc_percent(browser, evsel);
780

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

786
	nd = browser->curr_hot;
787

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

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

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

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

905
			if (browser->selection == NULL)
906
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
907
			else if (browser->selection->offset == -1)
908
				ui_helpline__puts("Actions are only available for assembly lines.");
909
			else if (!dl->ins.ops)
910
				goto show_sup_ins;
911
			else if (ins__is_ret(&dl->ins))
912
				goto out;
913
			else if (!(annotate_browser__jump(browser) ||
914
				     annotate_browser__callq(browser, evsel, hbt))) {
915
show_sup_ins:
916
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
917
			}
918
			continue;
919
		}
920
		case 't':
921 922 923 924 925
			if (notes->options->show_total_period) {
				notes->options->show_total_period = false;
				notes->options->show_nr_samples = true;
			} else if (notes->options->show_nr_samples)
				notes->options->show_nr_samples = false;
926
			else
927
				notes->options->show_total_period = true;
928 929
			annotate_browser__update_addr_width(browser);
			continue;
930 931
		case K_LEFT:
		case K_ESC:
932 933
		case 'q':
		case CTRL('c'):
934
			goto out;
935 936
		default:
			continue;
937
		}
938 939

		if (nd != NULL)
940
			annotate_browser__set_rb_top(browser, nd);
941 942
	}
out:
943
	ui_browser__hide(&browser->b);
944
	return key;
945 946
}

947 948 949
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
950
	/* Set default value for show_total_period and show_nr_samples  */
951
	annotate_browser__opts.show_total_period =
952 953 954
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
955

956 957 958
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

959
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
960
			     struct hist_browser_timer *hbt)
961
{
962 963 964 965
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

966
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
967 968
}

969 970 971 972
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
973 974
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
975
	struct annotation *notes = symbol__annotation(sym);
976 977 978 979

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

	for (offset = 0; offset < size; ++offset) {
982
		struct annotation_line *al = notes->offsets[offset];
983 984
		struct disasm_line *dl;
		struct browser_line *blt;
985

986 987
		dl = disasm_line(al);

988
		if (!disasm_line__is_valid_jump(dl, sym))
989 990
			continue;

991
		al = notes->offsets[dl->ops.target.offset];
992

993 994 995 996
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
997
		if (al == NULL)
998 999
			continue;

1000 1001 1002
		blt = browser_line(al);
		if (++blt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = blt->jump_sources;
1003 1004

		++browser->nr_jumps;
1005 1006 1007
	}
}

1008 1009 1010 1011 1012 1013 1014 1015 1016
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

1017 1018
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
1019
			 struct hist_browser_timer *hbt)
1020
{
1021
	struct annotation_line *al;
1022
	struct annotation *notes = symbol__annotation(sym);
1023
	size_t size;
1024 1025 1026 1027
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
1028 1029
	struct annotate_browser browser = {
		.b = {
1030
			.refresh = annotate_browser__refresh,
1031 1032
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
1033
			.filter  = disasm_line__filter,
1034
			.priv	 = &ms,
1035
			.use_navkeypressed = true,
1036
		},
1037
	};
1038
	int ret = -1, err;
1039
	int nr_pcnt = 1;
1040

1041
	if (sym == NULL)
1042 1043
		return -1;

1044 1045
	size = symbol__size(sym);

1046
	if (map->dso->annotate_warned)
1047 1048
		return -1;

1049 1050
	notes->options = &annotate_browser__opts;

1051 1052
	notes->offsets = zalloc(size * sizeof(struct annotation_line *));
	if (notes->offsets == NULL) {
1053 1054 1055 1056
		ui__error("Not enough memory!");
		return -1;
	}

1057
	if (perf_evsel__is_group_event(evsel))
1058 1059
		nr_pcnt = evsel->nr_members;

1060
	err = symbol__annotate(sym, map, evsel, sizeof(struct browser_line), &browser.arch);
1061 1062 1063 1064
	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);
1065
		goto out_free_offsets;
1066 1067
	}

1068 1069
	symbol__calc_percent(sym, evsel);

1070
	ui_helpline__push("Press ESC to exit");
1071

1072
	browser.start = map__rip_2objdump(map, sym->start);
1073

1074
	list_for_each_entry(al, &notes->src->source, node) {
1075
		struct browser_line *bpos;
1076
		size_t line_len = strlen(al->line);
1077

1078 1079
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1080
		bpos = browser_line(al);
1081
		bpos->idx = browser.nr_entries++;
1082
		if (al->offset != -1) {
1083
			bpos->idx_asm = browser.nr_asm_entries++;
1084 1085 1086 1087 1088 1089 1090
			/*
			 * 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
 			 */
1091
			if (al->offset < (s64)size)
1092
				notes->offsets[al->offset] = al;
1093
		} else
1094
			bpos->idx_asm = -1;
1095 1096
	}

1097
	annotate_browser__mark_jump_targets(&browser, size);
1098
	annotation__compute_ipc(notes, size);
1099

1100
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1101
	browser.max_addr_width = hex_width(sym->end);
1102
	browser.jumps_width = width_jumps(browser.max_jump_sources);
1103
	notes->nr_events = nr_pcnt;
1104
	browser.b.nr_entries = browser.nr_entries;
1105
	browser.b.entries = &notes->src->source,
1106
	browser.b.width += 18; /* Percentage */
1107

1108
	if (notes->options->hide_src_code)
1109 1110 1111 1112
		annotate_browser__init_asm_mode(&browser);

	annotate_browser__update_addr_width(&browser);

1113
	ret = annotate_browser__run(&browser, evsel, hbt);
1114 1115

	annotated_source__purge(notes->src);
1116 1117

out_free_offsets:
1118
	zfree(&notes->offsets);
1119 1120
	return ret;
}
1121 1122 1123

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

1125 1126 1127
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1128
static struct annotate_config {
1129 1130 1131 1132 1133
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1134
	ANNOTATE_CFG(show_linenr),
1135
	ANNOTATE_CFG(show_nr_jumps),
1136
	ANNOTATE_CFG(show_nr_samples),
1137
	ANNOTATE_CFG(show_total_period),
1138
	ANNOTATE_CFG(use_offset),
1139 1140 1141 1142 1143 1144
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1145
	const struct annotate_config *cfg = cfgp;
1146 1147 1148 1149

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

1150 1151
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1152
{
1153
	struct annotate_config *cfg;
1154 1155
	const char *name;

1156
	if (!strstarts(var, "annotate."))
1157 1158 1159 1160
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1161
		      sizeof(struct annotate_config), annotate_config__cmp);
1162 1163

	if (cfg == NULL)
1164 1165 1166
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1167 1168 1169 1170 1171 1172 1173
	return 0;
}

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