annotate.c 28.8 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
static struct annotation_options annotate_browser__opts = {
26 27 28 29
	.use_offset	= true,
	.jump_arrows	= true,
};

30 31
struct arch;

32
struct annotate_browser {
33 34 35 36 37 38 39 40 41 42 43 44 45 46
	struct ui_browser	    b;
	struct rb_root		    entries;
	struct rb_node		   *curr_hot;
	struct annotation_line	   *selection;
	struct arch		   *arch;
	int			    nr_asm_entries;
	int			    nr_entries;
	bool			    searching_backwards;
	u8			    addr_width;
	u8			    jumps_width;
	u8			    target_width;
	u8			    min_addr_width;
	u8			    max_addr_width;
	char			    search_bf[128];
47 48
};

49 50 51 52 53 54
static inline struct annotation *browser__annotation(struct ui_browser *browser)
{
	struct map_symbol *ms = browser->priv;
	return symbol__annotation(ms->sym);
}

55
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
56
{
57
	struct annotation *notes = browser__annotation(browser);
58 59

	if (notes->options->hide_src_code) {
60 61 62
		struct annotation_line *al = list_entry(entry, struct annotation_line, node);

		return al->offset == -1;
63 64 65 66 67
	}

	return false;
}

68
static int ui_browser__jumps_percent_color(struct ui_browser *browser, int nr, bool current)
69
{
70
	struct annotation *notes = browser__annotation(browser);
71

72
	if (current && (!browser->use_navkeypressed || browser->navkeypressed))
73
		return HE_COLORSET_SELECTED;
74
	if (nr == notes->max_jump_sources)
75 76 77 78 79 80
		return HE_COLORSET_TOP;
	if (nr > 1)
		return HE_COLORSET_MEDIUM;
	return HE_COLORSET_NORMAL;
}

81
static int ui_browser__set_jumps_percent_color(struct ui_browser *browser, int nr, bool current)
82
{
83 84
	 int color = ui_browser__jumps_percent_color(browser, nr, current);
	 return ui_browser__set_color(browser, color);
85 86
}

87 88 89
static void disasm_line__write(struct disasm_line *dl, struct ui_browser *browser,
			       char *bf, size_t size)
{
90
	struct annotation *notes = browser__annotation(browser);
91

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

112
	disasm_line__scnprintf(dl, bf, size, !notes->options->use_offset);
113 114
}

115
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
116
{
117
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
118
	struct annotation *notes = browser__annotation(browser);
119
	struct annotation_line *al = list_entry(entry, struct annotation_line, node);
120
	bool current_entry = ui_browser__is_current_entry(browser, row);
121
	bool change_color = (!notes->options->hide_src_code &&
122 123 124
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
125
	int i, pcnt_width = annotation__pcnt_width(notes),
126
	       cycles_width = annotation__cycles_width(notes);
127
	double percent_max = 0.0;
128
	char bf[256];
129
	bool show_title = false;
130

131
	for (i = 0; i < notes->nr_events; i++) {
132 133
		if (al->samples[i].percent > percent_max)
			percent_max = al->samples[i].percent;
134 135
	}

136
	if ((row == 0) && (al->offset == -1 || percent_max == 0.0)) {
137
		if (notes->have_cycles) {
138
			if (al->ipc == 0.0 && al->cycles == 0)
139 140 141 142 143
				show_title = true;
		} else
			show_title = true;
	}

144
	if (al->offset != -1 && percent_max != 0.0) {
145
		for (i = 0; i < notes->nr_events; i++) {
146
			ui_browser__set_percent_color(browser,
147
						al->samples[i].percent,
148
						current_entry);
149
			if (notes->options->show_total_period) {
150
				ui_browser__printf(browser, "%11" PRIu64 " ",
151
						   al->samples[i].he.period);
152
			} else if (notes->options->show_nr_samples) {
153
				ui_browser__printf(browser, "%6" PRIu64 " ",
154
						   al->samples[i].he.nr_samples);
155 156
			} else {
				ui_browser__printf(browser, "%6.2f ",
157
						   al->samples[i].percent);
158
			}
159
		}
160
	} else {
161
		ui_browser__set_percent_color(browser, 0, current_entry);
162 163

		if (!show_title)
164
			ui_browser__write_nstring(browser, " ", pcnt_width);
165 166
		else {
			ui_browser__printf(browser, "%*s", pcnt_width,
167 168
					   notes->options->show_total_period ? "Period" :
					   notes->options->show_nr_samples ? "Samples" : "Percent");
169
		}
170
	}
171
	if (notes->have_cycles) {
172
		if (al->ipc)
173
			ui_browser__printf(browser, "%*.2f ", ANNOTATION__IPC_WIDTH - 1, al->ipc);
174
		else if (!show_title)
175
			ui_browser__write_nstring(browser, " ", ANNOTATION__IPC_WIDTH);
176
		else
177
			ui_browser__printf(browser, "%*s ", ANNOTATION__IPC_WIDTH - 1, "IPC");
178

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

188
	SLsmg_write_char(' ');
189 190

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

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

209
		if (!notes->options->use_offset)
210
			addr += notes->start;
211

212
		if (!notes->options->use_offset) {
213
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
214
		} else {
215
			if (al->jump_sources) {
216
				if (notes->options->show_nr_jumps) {
217 218 219
					int prev;
					printed = scnprintf(bf, sizeof(bf), "%*d ",
							    ab->jumps_width,
220 221
							    al->jump_sources);
					prev = ui_browser__set_jumps_percent_color(browser, al->jump_sources,
222
										   current_entry);
223
					ui_browser__write_nstring(browser, bf, printed);
224
					ui_browser__set_color(browser, prev);
225 226
				}

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

235
		if (change_color)
236
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
237
		ui_browser__write_nstring(browser, bf, printed);
238
		if (change_color)
239
			ui_browser__set_color(browser, color);
240

241 242
		disasm_line__write(disasm_line(al), browser, bf, sizeof(bf));

243
		ui_browser__write_nstring(browser, bf, width - pcnt_width - cycles_width - 3 - printed);
244
	}
245

246
	if (current_entry)
247
		ab->selection = al;
248 249
}

250 251
static bool is_fused(struct annotate_browser *ab, struct disasm_line *cursor)
{
252
	struct disasm_line *pos = list_prev_entry(cursor, al.node);
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
	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);
}

269
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
270 271
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
272
	struct disasm_line *cursor = disasm_line(ab->selection);
273
	struct annotation_line *target;
274
	unsigned int from, to;
275 276
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;
277
	struct annotation *notes = symbol__annotation(sym);
278
	u8 pcnt_width = annotation__pcnt_width(notes);
279
	int width;
280 281 282 283

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

285
	if (!disasm_line__is_valid_jump(cursor, sym))
286
		return;
287

288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307
	/*
	 * 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.
	 */
308
	target = notes->offsets[cursor->ops.target.offset];
309
	if (target == NULL) {
310
		ui_helpline__printf("WARN: jump target inconsistency, press 'o', notes->offsets[%#x] = NULL\n",
311 312 313
				    cursor->ops.target.offset);
		return;
	}
314

315
	if (notes->options->hide_src_code) {
316 317
		from = cursor->al.idx_asm;
		to = target->idx_asm;
318
	} else {
319 320
		from = (u64)cursor->al.idx;
		to = (u64)target->idx;
321 322
	}

323
	width = annotation__cycles_width(notes);
J
Jin Yao 已提交
324

325
	ui_browser__set_color(browser, HE_COLORSET_JUMP_ARROWS);
J
Jin Yao 已提交
326 327
	__ui_browser__line_arrow(browser,
				 pcnt_width + 2 + ab->addr_width + width,
328
				 from, to);
329 330 331

	if (is_fused(ab, cursor)) {
		ui_browser__mark_fused(browser,
J
Jin Yao 已提交
332
				       pcnt_width + 3 + ab->addr_width + width,
333 334 335
				       from - 1,
				       to > from ? true : false);
	}
336 337 338 339
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
340
	struct annotation *notes = browser__annotation(browser);
341
	int ret = ui_browser__list_head_refresh(browser);
342
	int pcnt_width = annotation__pcnt_width(notes);
343

344
	if (notes->options->jump_arrows)
345
		annotate_browser__draw_current_jump(browser);
346

347
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
348
	__ui_browser__vline(browser, pcnt_width, 0, browser->height - 1);
349 350 351
	return ret;
}

352
static int disasm__cmp(struct annotation_line *a, struct annotation_line *b)
353 354 355
{
	int i;

356
	for (i = 0; i < a->samples_nr; i++) {
357
		if (a->samples[i].percent == b->samples[i].percent)
358
			continue;
359
		return a->samples[i].percent < b->samples[i].percent;
360 361 362 363
	}
	return 0;
}

364
static void disasm_rb_tree__insert(struct rb_root *root, struct annotation_line *al)
365
{
366
	struct rb_node **p = &root->rb_node;
367
	struct rb_node *parent = NULL;
368
	struct annotation_line *l;
369 370 371

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

374
		if (disasm__cmp(al, l))
375 376 377 378
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
379 380
	rb_link_node(&al->rb_node, parent, p);
	rb_insert_color(&al->rb_node, root);
381 382
}

383
static void annotate_browser__set_top(struct annotate_browser *browser,
384
				      struct annotation_line *pos, u32 idx)
385 386 387
{
	unsigned back;

388 389 390
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
391

392
	while (browser->b.top_idx != 0 && back != 0) {
393
		pos = list_entry(pos->node.prev, struct annotation_line, node);
394

395
		if (disasm_line__filter(&browser->b, &pos->node))
396 397
			continue;

398
		--browser->b.top_idx;
399 400 401
		--back;
	}

402
	browser->b.top = pos;
403
	browser->b.navkeypressed = true;
404 405 406 407 408
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
409
	struct annotation *notes = browser__annotation(&browser->b);
410 411
	struct annotation_line * pos = rb_entry(nd, struct annotation_line, rb_node);
	u32 idx = pos->idx;
412

413
	if (notes->options->hide_src_code)
414
		idx = pos->idx_asm;
415
	annotate_browser__set_top(browser, pos, idx);
416
	browser->curr_hot = nd;
417 418
}

419
static void annotate_browser__calc_percent(struct annotate_browser *browser,
420
					   struct perf_evsel *evsel)
421
{
422 423
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
424
	struct annotation *notes = symbol__annotation(sym);
425
	struct disasm_line *pos;
426 427 428 429 430

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

431 432
	symbol__calc_percent(sym, evsel);

433
	list_for_each_entry(pos, &notes->src->source, al.node) {
434 435
		double max_percent = 0.0;
		int i;
436

437
		if (pos->al.offset == -1) {
438
			RB_CLEAR_NODE(&pos->al.rb_node);
439 440 441
			continue;
		}

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

445 446
			if (max_percent < sample->percent)
				max_percent = sample->percent;
447 448
		}

449
		if (max_percent < 0.01 && pos->al.ipc == 0) {
450
			RB_CLEAR_NODE(&pos->al.rb_node);
451 452
			continue;
		}
453
		disasm_rb_tree__insert(&browser->entries, &pos->al);
454 455 456 457 458 459
	}
	pthread_mutex_unlock(&notes->lock);

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

460 461
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
462
	struct annotation *notes = browser__annotation(&browser->b);
463
	struct annotation_line *al;
464 465 466
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
467
	al = list_entry(browser->b.top, struct annotation_line, node);
468

469
	if (notes->options->hide_src_code) {
470 471
		if (al->idx_asm < offset)
			offset = al->idx;
472 473

		browser->b.nr_entries = browser->nr_entries;
474
		notes->options->hide_src_code = false;
475
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
476 477
		browser->b.top_idx = al->idx - offset;
		browser->b.index = al->idx;
478
	} else {
479
		if (al->idx_asm < 0) {
480 481 482 483 484
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

485 486
		if (al->idx_asm < offset)
			offset = al->idx_asm;
487 488

		browser->b.nr_entries = browser->nr_asm_entries;
489
		notes->options->hide_src_code = true;
490
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
491 492
		browser->b.top_idx = al->idx_asm - offset;
		browser->b.index = al->idx_asm;
493 494 495 496 497
	}

	return true;
}

498 499 500 501 502 503
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;
}

504 505 506 507 508 509 510 511
#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);
}

512 513
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
514
				    struct hist_browser_timer *hbt)
515 516
{
	struct map_symbol *ms = browser->b.priv;
517
	struct disasm_line *dl = disasm_line(browser->selection);
518
	struct annotation *notes;
519
	char title[SYM_TITLE_MAX_SIZE];
520

521
	if (!ins__is_call(&dl->ins))
522 523
		return false;

524
	if (!dl->ops.target.sym) {
525 526 527 528
		ui_helpline__puts("The called function was not found.");
		return true;
	}

529
	notes = symbol__annotation(dl->ops.target.sym);
530 531
	pthread_mutex_lock(&notes->lock);

532
	if (notes->src == NULL && symbol__alloc_hist(dl->ops.target.sym) < 0) {
533 534
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
535
			    dl->ops.target.sym->name);
536 537 538 539
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
540
	symbol__tui_annotate(dl->ops.target.sym, ms->map, evsel, hbt);
541
	sym_title(ms->sym, ms->map, title, sizeof(title));
542
	ui_browser__show_title(&browser->b, title);
543 544 545
	return true;
}

546 547 548
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
549
{
550
	struct annotation *notes = browser__annotation(&browser->b);
551
	struct disasm_line *pos;
552 553

	*idx = 0;
554
	list_for_each_entry(pos, &notes->src->source, al.node) {
555
		if (pos->al.offset == offset)
556
			return pos;
557
		if (!disasm_line__filter(&browser->b, &pos->al.node))
558 559 560 561 562 563 564 565
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
566
	struct disasm_line *dl = disasm_line(browser->selection);
567
	u64 offset;
568
	s64 idx;
569

570
	if (!ins__is_jump(&dl->ins))
571 572
		return false;

573 574
	offset = dl->ops.target.offset;
	dl = annotate_browser__find_offset(browser, offset, &idx);
575
	if (dl == NULL) {
576
		ui_helpline__printf("Invalid jump offset: %" PRIx64, offset);
577 578 579
		return true;
	}

580
	annotate_browser__set_top(browser, &dl->al, idx);
581

582 583 584
	return true;
}

585
static
586
struct annotation_line *annotate_browser__find_string(struct annotate_browser *browser,
587
					  char *s, s64 *idx)
588
{
589
	struct annotation *notes = browser__annotation(&browser->b);
590
	struct annotation_line *al = browser->selection;
591 592

	*idx = browser->b.index;
593 594
	list_for_each_entry_continue(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
595 596 597 598
			continue;

		++*idx;

599 600
		if (al->line && strstr(al->line, s) != NULL)
			return al;
601 602 603 604 605 606 607
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
608
	struct annotation_line *al;
609 610
	s64 idx;

611 612
	al = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (al == NULL) {
613 614 615 616
		ui_helpline__puts("String not found!");
		return false;
	}

617
	annotate_browser__set_top(browser, al, idx);
618 619 620 621
	browser->searching_backwards = false;
	return true;
}

622
static
623
struct annotation_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
624
						  char *s, s64 *idx)
625
{
626
	struct annotation *notes = browser__annotation(&browser->b);
627
	struct annotation_line *al = browser->selection;
628 629

	*idx = browser->b.index;
630 631
	list_for_each_entry_continue_reverse(al, &notes->src->source, node) {
		if (disasm_line__filter(&browser->b, &al->node))
632 633 634 635
			continue;

		--*idx;

636 637
		if (al->line && strstr(al->line, s) != NULL)
			return al;
638 639 640 641 642 643 644
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
645
	struct annotation_line *al;
646 647
	s64 idx;

648 649
	al = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (al == NULL) {
650 651 652 653
		ui_helpline__puts("String not found!");
		return false;
	}

654
	annotate_browser__set_top(browser, al, idx);
655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 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
	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);
}

707 708
static void annotate_browser__update_addr_width(struct annotate_browser *browser)
{
709
	struct annotation *notes = browser__annotation(&browser->b);
710 711

	if (notes->options->use_offset)
712 713 714 715 716 717
		browser->target_width = browser->min_addr_width;
	else
		browser->target_width = browser->max_addr_width;

	browser->addr_width = browser->target_width;

718
	if (notes->options->show_nr_jumps)
719 720 721
		browser->addr_width += browser->jumps_width + 1;
}

722 723
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
724
				 struct hist_browser_timer *hbt)
725 726
{
	struct rb_node *nd = NULL;
727
	struct map_symbol *ms = browser->b.priv;
728
	struct symbol *sym = ms->sym;
729
	struct annotation *notes = symbol__annotation(ms->sym);
730
	const char *help = "Press 'h' for help on key bindings";
731
	int delay_secs = hbt ? hbt->refresh : 0;
732
	int key;
733
	char title[SYM_TITLE_MAX_SIZE];
734

735 736
	sym_title(sym, ms->map, title, sizeof(title));
	if (ui_browser__show(&browser->b, title, help) < 0)
737
		return -1;
738

739
	annotate_browser__calc_percent(browser, evsel);
740

741 742 743
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
744
	}
745

746
	nd = browser->curr_hot;
747

748
	while (1) {
749
		key = ui_browser__run(&browser->b, delay_secs);
750

751
		if (delay_secs != 0) {
752
			annotate_browser__calc_percent(browser, evsel);
753 754 755 756 757 758 759 760 761
			/*
			 * 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;
		}

762
		switch (key) {
763
		case K_TIMER:
764 765
			if (hbt)
				hbt->timer(hbt->arg);
766 767

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

865
			if (browser->selection == NULL)
866
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
867
			else if (browser->selection->offset == -1)
868
				ui_helpline__puts("Actions are only available for assembly lines.");
869
			else if (!dl->ins.ops)
870
				goto show_sup_ins;
871
			else if (ins__is_ret(&dl->ins))
872
				goto out;
873
			else if (!(annotate_browser__jump(browser) ||
874
				     annotate_browser__callq(browser, evsel, hbt))) {
875
show_sup_ins:
876
				ui_helpline__puts("Actions are only available for function call/return & jump/branch instructions.");
877
			}
878
			continue;
879
		}
880
		case 't':
881 882 883 884 885
			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;
886
			else
887
				notes->options->show_total_period = true;
888 889
			annotate_browser__update_addr_width(browser);
			continue;
890 891
		case K_LEFT:
		case K_ESC:
892 893
		case 'q':
		case CTRL('c'):
894
			goto out;
895 896
		default:
			continue;
897
		}
898 899

		if (nd != NULL)
900
			annotate_browser__set_rb_top(browser, nd);
901 902
	}
out:
903
	ui_browser__hide(&browser->b);
904
	return key;
905 906
}

907 908 909
int map_symbol__tui_annotate(struct map_symbol *ms, struct perf_evsel *evsel,
			     struct hist_browser_timer *hbt)
{
910
	/* Set default value for show_total_period and show_nr_samples  */
911
	annotate_browser__opts.show_total_period =
912 913 914
		symbol_conf.show_total_period;
	annotate_browser__opts.show_nr_samples =
		symbol_conf.show_nr_samples;
915

916 917 918
	return symbol__tui_annotate(ms->sym, ms->map, evsel, hbt);
}

919
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
920
			     struct hist_browser_timer *hbt)
921
{
922 923 924 925
	/* reset abort key so that it can get Ctrl-C as a key */
	SLang_reset_tty();
	SLang_init_tty(0, 0, 0);

926
	return map_symbol__tui_annotate(&he->ms, evsel, hbt);
927 928
}

929 930 931 932 933 934 935 936 937
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

938 939
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
940
			 struct hist_browser_timer *hbt)
941
{
942
	struct annotation_line *al;
943
	struct annotation *notes = symbol__annotation(sym);
944
	size_t size;
945 946 947 948
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
949 950
	struct annotate_browser browser = {
		.b = {
951
			.refresh = annotate_browser__refresh,
952 953
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
954
			.filter  = disasm_line__filter,
955
			.priv	 = &ms,
956
			.use_navkeypressed = true,
957
		},
958
	};
959
	int ret = -1, err;
960
	int nr_pcnt = 1;
961

962
	if (sym == NULL)
963 964
		return -1;

965 966
	size = symbol__size(sym);

967
	if (map->dso->annotate_warned)
968 969
		return -1;

970 971
	notes->options = &annotate_browser__opts;

972 973
	notes->offsets = zalloc(size * sizeof(struct annotation_line *));
	if (notes->offsets == NULL) {
974 975 976 977
		ui__error("Not enough memory!");
		return -1;
	}

978
	if (perf_evsel__is_group_event(evsel))
979 980
		nr_pcnt = evsel->nr_members;

981
	err = symbol__annotate(sym, map, evsel, 0, &browser.arch);
982 983 984 985
	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);
986
		goto out_free_offsets;
987 988
	}

989 990
	symbol__calc_percent(sym, evsel);

991
	ui_helpline__push("Press ESC to exit");
992

993
	notes->start = map__rip_2objdump(map, sym->start);
994

995 996
	list_for_each_entry(al, &notes->src->source, node) {
		size_t line_len = strlen(al->line);
997

998 999
		if (browser.b.width < line_len)
			browser.b.width = line_len;
1000
		al->idx = browser.nr_entries++;
1001
		if (al->offset != -1) {
1002
			al->idx_asm = browser.nr_asm_entries++;
1003 1004 1005 1006 1007 1008 1009
			/*
			 * 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
 			 */
1010
			if (al->offset < (s64)size)
1011
				notes->offsets[al->offset] = al;
1012
		} else
1013
			al->idx_asm = -1;
1014 1015
	}

1016
	annotation__mark_jump_targets(notes, sym);
1017
	annotation__compute_ipc(notes, size);
1018

1019
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
1020
	browser.max_addr_width = hex_width(sym->end);
1021
	browser.jumps_width = width_jumps(notes->max_jump_sources);
1022
	notes->nr_events = nr_pcnt;
1023
	browser.b.nr_entries = browser.nr_entries;
1024
	browser.b.entries = &notes->src->source,
1025
	browser.b.width += 18; /* Percentage */
1026

1027
	if (notes->options->hide_src_code)
1028 1029 1030 1031
		annotate_browser__init_asm_mode(&browser);

	annotate_browser__update_addr_width(&browser);

1032
	ret = annotate_browser__run(&browser, evsel, hbt);
1033 1034

	annotated_source__purge(notes->src);
1035 1036

out_free_offsets:
1037
	zfree(&notes->offsets);
1038 1039
	return ret;
}
1040 1041 1042

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

1044 1045 1046
/*
 * Keep the entries sorted, they are bsearch'ed
 */
1047
static struct annotate_config {
1048 1049 1050 1051 1052
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
1053
	ANNOTATE_CFG(show_linenr),
1054
	ANNOTATE_CFG(show_nr_jumps),
1055
	ANNOTATE_CFG(show_nr_samples),
1056
	ANNOTATE_CFG(show_total_period),
1057
	ANNOTATE_CFG(use_offset),
1058 1059 1060 1061 1062 1063
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
1064
	const struct annotate_config *cfg = cfgp;
1065 1066 1067 1068

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

1069 1070
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
1071
{
1072
	struct annotate_config *cfg;
1073 1074
	const char *name;

1075
	if (!strstarts(var, "annotate."))
1076 1077 1078 1079
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
1080
		      sizeof(struct annotate_config), annotate_config__cmp);
1081 1082

	if (cfg == NULL)
1083 1084 1085
		ui__warning("%s variable unknown, ignoring...", var);
	else
		*cfg->value = perf_config_bool(name, value);
1086 1087 1088 1089 1090 1091 1092
	return 0;
}

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