annotate.c 24.9 KB
Newer Older
1
#include "../../util/util.h"
2 3 4
#include "../browser.h"
#include "../helpline.h"
#include "../libslang.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 <pthread.h>
13
#include <newt.h>
14

15 16 17 18
struct browser_disasm_line {
	struct rb_node	rb_node;
	u32		idx;
	int		idx_asm;
19
	int		jump_sources;
20
	double		percent[1];
21 22
};

23 24 25 26 27 28 29 30 31 32
static struct annotate_browser_opt {
	bool hide_src_code,
	     use_offset,
	     jump_arrows,
	     show_nr_jumps;
} annotate_browser__opts = {
	.use_offset	= true,
	.jump_arrows	= true,
};

33 34 35
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
36
	struct rb_node	  *curr_hot;
37
	struct disasm_line	  *selection;
38
	struct disasm_line  **offsets;
39
	u64		    start;
40 41
	int		    nr_asm_entries;
	int		    nr_entries;
42 43
	int		    max_jump_sources;
	int		    nr_jumps;
44
	bool		    searching_backwards;
45
	u8		    addr_width;
46 47
	u8		    jumps_width;
	u8		    target_width;
48 49
	u8		    min_addr_width;
	u8		    max_addr_width;
50
	char		    search_bf[128];
51 52
};

53
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
54
{
55
	return (struct browser_disasm_line *)(dl + 1);
56 57
}

58 59
static bool disasm_line__filter(struct ui_browser *browser __maybe_unused,
				void *entry)
60
{
61
	if (annotate_browser__opts.hide_src_code) {
62 63
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
64 65 66 67 68
	}

	return false;
}

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87
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);
}

88
static void annotate_browser__write(struct ui_browser *browser, void *entry, int row)
89
{
90
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
91
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
92
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
93
	bool current_entry = ui_browser__is_current_entry(browser, row);
94
	bool change_color = (!annotate_browser__opts.hide_src_code &&
95 96 97
			     (!current_entry || (browser->use_navkeypressed &&
					         !browser->navkeypressed)));
	int width = browser->width, printed;
98
	char bf[256];
99

100 101 102
	if (dl->offset != -1 && bdl->percent[0] != 0.0) {
		ui_browser__set_percent_color(browser, bdl->percent[0], current_entry);
		slsmg_printf("%6.2f ", bdl->percent[0]);
103
	} else {
104
		ui_browser__set_percent_color(browser, 0, current_entry);
105
		slsmg_write_nstring(" ", 7);
106 107
	}

108
	SLsmg_write_char(' ');
109 110

	/* The scroll bar isn't being used */
111
	if (!browser->navkeypressed)
112 113
		width += 1;

114
	if (!*dl->line)
115
		slsmg_write_nstring(" ", width - 7);
116 117 118 119 120 121
	else if (dl->offset == -1) {
		printed = scnprintf(bf, sizeof(bf), "%*s  ",
				    ab->addr_width, " ");
		slsmg_write_nstring(bf, printed);
		slsmg_write_nstring(dl->line, width - printed - 6);
	} else {
122
		u64 addr = dl->offset;
123
		int color = -1;
124

125
		if (!annotate_browser__opts.use_offset)
126 127
			addr += ab->start;

128
		if (!annotate_browser__opts.use_offset) {
129
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ": ", addr);
130
		} else {
131
			if (bdl->jump_sources) {
132
				if (annotate_browser__opts.show_nr_jumps) {
133 134 135 136 137 138 139
					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);
					slsmg_write_nstring(bf, printed);
140
					ui_browser__set_color(browser, prev);
141 142
				}

143
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ": ",
144
						    ab->target_width, addr);
145
			} else {
146 147
				printed = scnprintf(bf, sizeof(bf), "%*s  ",
						    ab->addr_width, " ");
148 149
			}
		}
150

151
		if (change_color)
152
			color = ui_browser__set_color(browser, HE_COLORSET_ADDR);
153 154
		slsmg_write_nstring(bf, printed);
		if (change_color)
155
			ui_browser__set_color(browser, color);
156
		if (dl->ins && dl->ins->ops->scnprintf) {
157
			if (ins__is_jump(dl->ins)) {
158
				bool fwd = dl->ops.target.offset > (u64)dl->offset;
159

160
				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
161
								    SLSMG_UARROW_CHAR);
162
				SLsmg_write_char(' ');
163
			} else if (ins__is_call(dl->ins)) {
164
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
165
				SLsmg_write_char(' ');
166 167 168
			} else {
				slsmg_write_nstring(" ", 2);
			}
169 170 171 172
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
173
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
174 175 176
				SLsmg_write_char(' ');
			}
		}
177

178
		disasm_line__scnprintf(dl, bf, sizeof(bf), !annotate_browser__opts.use_offset);
179
		slsmg_write_nstring(bf, width - 10 - printed);
180
	}
181

182
	if (current_entry)
183
		ab->selection = dl;
184 185
}

186 187 188 189 190 191 192 193 194 195
static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
	if (!dl || !dl->ins || !ins__is_jump(dl->ins)
	    || !disasm_line__has_offset(dl)
	    || dl->ops.target.offset >= symbol__size(sym))
		return false;

	return true;
}

196
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
197 198
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
199 200
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
201
	unsigned int from, to;
202 203 204 205 206 207
	struct map_symbol *ms = ab->b.priv;
	struct symbol *sym = ms->sym;

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

209
	if (!disasm_line__is_valid_jump(cursor, sym))
210
		return;
211

212 213 214
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
215

216 217
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
218

219
	if (annotate_browser__opts.hide_src_code) {
220
		from = bcursor->idx_asm;
221 222
		to = btarget->idx_asm;
	} else {
223
		from = (u64)bcursor->idx;
224 225 226 227
		to = (u64)btarget->idx;
	}

	ui_browser__set_color(browser, HE_COLORSET_CODE);
228
	__ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
229 230 231 232 233 234
}

static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
	int ret = ui_browser__list_head_refresh(browser);

235
	if (annotate_browser__opts.jump_arrows)
236
		annotate_browser__draw_current_jump(browser);
237

238 239
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
	__ui_browser__vline(browser, 7, 0, browser->height - 1);
240 241 242
	return ret;
}

243
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
244 245 246
{
	double percent = 0.0;

247
	if (dl->offset != -1) {
248
		int len = sym->end - sym->start;
249
		unsigned int hits = 0;
250
		struct annotation *notes = symbol__annotation(sym);
251
		struct source_line *src_line = notes->src->lines;
252
		struct sym_hist *h = annotation__histogram(notes, evidx);
253 254
		s64 offset = dl->offset;
		struct disasm_line *next;
255

256
		next = disasm__get_next_ip_line(&notes->src->source, dl);
257 258
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
259
			if (src_line) {
260
				percent += src_line[offset].p[0].percent;
261
			} else
262
				hits += h->addr[offset];
263 264 265

			++offset;
		}
266 267 268 269 270
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
271 272 273
			percent = 100.0 * hits / h->sum;
	}

274 275 276
	return percent;
}

277
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
278
{
279
	struct rb_node **p = &root->rb_node;
280
	struct rb_node *parent = NULL;
281
	struct browser_disasm_line *l;
282 283 284

	while (*p != NULL) {
		parent = *p;
285
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
286
		if (bdl->percent[0] < l->percent[0])
287 288 289 290
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
291 292
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
293 294
}

295
static void annotate_browser__set_top(struct annotate_browser *browser,
296
				      struct disasm_line *pos, u32 idx)
297 298 299
{
	unsigned back;

300 301 302
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
303

304
	while (browser->b.top_idx != 0 && back != 0) {
305
		pos = list_entry(pos->node.prev, struct disasm_line, node);
306

307
		if (disasm_line__filter(&browser->b, &pos->node))
308 309
			continue;

310
		--browser->b.top_idx;
311 312 313
		--back;
	}

314 315
	browser->b.top = pos;
	browser->b.navkeypressed = true;
316 317 318 319 320
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
321
	struct browser_disasm_line *bpos;
322
	struct disasm_line *pos;
323
	u32 idx;
324

325 326
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
327
	idx = bpos->idx;
328
	if (annotate_browser__opts.hide_src_code)
329 330
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
331
	browser->curr_hot = nd;
332 333
}

334
static void annotate_browser__calc_percent(struct annotate_browser *browser,
335
					   struct perf_evsel *evsel)
336
{
337 338
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
339
	struct annotation *notes = symbol__annotation(sym);
340
	struct disasm_line *pos;
341 342 343 344 345 346

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
347
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
348 349
		bpos->percent[0] = disasm_line__calc_percent(pos, sym, evsel->idx);
		if (bpos->percent[0] < 0.01) {
350
			RB_CLEAR_NODE(&bpos->rb_node);
351 352
			continue;
		}
353
		disasm_rb_tree__insert(&browser->entries, bpos);
354 355 356 357 358 359
	}
	pthread_mutex_unlock(&notes->lock);

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

360 361
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
362
	struct disasm_line *dl;
363
	struct browser_disasm_line *bdl;
364 365 366
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
367
	dl = list_entry(browser->b.top, struct disasm_line, node);
368
	bdl = disasm_line__browser(dl);
369

370
	if (annotate_browser__opts.hide_src_code) {
371 372
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
373 374

		browser->b.nr_entries = browser->nr_entries;
375
		annotate_browser__opts.hide_src_code = false;
376
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
377 378
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
379
	} else {
380
		if (bdl->idx_asm < 0) {
381 382 383 384 385
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

386 387
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
388 389

		browser->b.nr_entries = browser->nr_asm_entries;
390
		annotate_browser__opts.hide_src_code = true;
391
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
392 393
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
394 395 396 397 398
	}

	return true;
}

399 400 401 402 403 404
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;
}

405 406
static bool annotate_browser__callq(struct annotate_browser *browser,
				    struct perf_evsel *evsel,
407
				    struct hist_browser_timer *hbt)
408 409
{
	struct map_symbol *ms = browser->b.priv;
410
	struct disasm_line *dl = browser->selection;
411 412 413 414 415
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

416
	if (!ins__is_call(dl->ins))
417 418
		return false;

419
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436
	target = map__find_symbol(ms->map, ip, NULL);
	if (target == NULL) {
		ui_helpline__puts("The called function was not found.");
		return true;
	}

	notes = symbol__annotation(target);
	pthread_mutex_lock(&notes->lock);

	if (notes->src == NULL && symbol__alloc_hist(target) < 0) {
		pthread_mutex_unlock(&notes->lock);
		ui__warning("Not enough memory for annotating '%s' symbol!\n",
			    target->name);
		return true;
	}

	pthread_mutex_unlock(&notes->lock);
437
	symbol__tui_annotate(target, ms->map, evsel, hbt);
438 439 440 441
	ui_browser__show_title(&browser->b, sym->name);
	return true;
}

442 443 444
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
445 446 447 448
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
449
	struct disasm_line *pos;
450 451 452 453 454

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
455
		if (!disasm_line__filter(&browser->b, &pos->node))
456 457 458 459 460 461 462 463
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
464
	struct disasm_line *dl = browser->selection;
465
	s64 idx;
466

467
	if (!ins__is_jump(dl->ins))
468 469
		return false;

470
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
471
	if (dl == NULL) {
472 473 474 475
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

476
	annotate_browser__set_top(browser, dl, idx);
477 478 479 480
	
	return true;
}

481 482 483
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
484 485 486 487
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
488
	struct disasm_line *pos = browser->selection;
489 490 491

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
492
		if (disasm_line__filter(&browser->b, &pos->node))
493 494 495 496 497 498 499 500 501 502 503 504 505
			continue;

		++*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
506
	struct disasm_line *dl;
507 508
	s64 idx;

509 510
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
511 512 513 514
		ui_helpline__puts("String not found!");
		return false;
	}

515
	annotate_browser__set_top(browser, dl, idx);
516 517 518 519
	browser->searching_backwards = false;
	return true;
}

520 521 522
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
523 524 525 526
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
527
	struct disasm_line *pos = browser->selection;
528 529 530

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
531
		if (disasm_line__filter(&browser->b, &pos->node))
532 533 534 535 536 537 538 539 540 541 542 543 544
			continue;

		--*idx;

		if (pos->line && strstr(pos->line, s) != NULL)
			return pos;
	}

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
545
	struct disasm_line *dl;
546 547
	s64 idx;

548 549
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
550 551 552 553
		ui_helpline__puts("String not found!");
		return false;
	}

554
	annotate_browser__set_top(browser, dl, idx);
555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606
	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);
}

607 608 609 610 611 612 613 614 615 616 617 618 619
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;
}

620 621
static int annotate_browser__run(struct annotate_browser *browser,
				 struct perf_evsel *evsel,
622
				 struct hist_browser_timer *hbt)
623 624
{
	struct rb_node *nd = NULL;
625
	struct map_symbol *ms = browser->b.priv;
626
	struct symbol *sym = ms->sym;
627
	const char *help = "Press 'h' for help on key bindings";
628
	int delay_secs = hbt ? hbt->refresh : 0;
629
	int key;
630

631
	if (ui_browser__show(&browser->b, sym->name, help) < 0)
632
		return -1;
633

634
	annotate_browser__calc_percent(browser, evsel);
635

636 637 638
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
639
	}
640

641
	nd = browser->curr_hot;
642

643
	while (1) {
644
		key = ui_browser__run(&browser->b, delay_secs);
645

646
		if (delay_secs != 0) {
647
			annotate_browser__calc_percent(browser, evsel);
648 649 650 651 652 653 654 655 656
			/*
			 * 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;
		}

657
		switch (key) {
658
		case K_TIMER:
659 660
			if (hbt)
				hbt->timer(hbt->arg);
661 662

			if (delay_secs != 0)
663
				symbol__annotate_decay_histogram(sym, evsel->idx);
664
			continue;
665
		case K_TAB:
666 667 668
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
669
					nd = rb_last(&browser->entries);
670
			} else
671
				nd = browser->curr_hot;
672
			break;
673
		case K_UNTAB:
674 675 676
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
677
					nd = rb_first(&browser->entries);
678
			else
679
				nd = browser->curr_hot;
680
			break;
681
		case K_F1:
682
		case 'h':
683
			ui_browser__help_window(&browser->b,
684 685 686 687 688
		"UP/DOWN/PGUP\n"
		"PGDN/SPACE    Navigate\n"
		"q/ESC/CTRL+C  Exit\n\n"
		"->            Go to target\n"
		"<-            Exit\n"
689
		"H             Cycle thru hottest instructions\n"
690 691 692 693 694 695
		"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"
		"/             Search string\n"
696
		"r             Run available scripts\n"
697 698
		"?             Search previous string\n");
			continue;
699 700 701 702 703
		case 'r':
			{
				script_browse(NULL);
				continue;
			}
704
		case 'H':
705
			nd = browser->curr_hot;
706
			break;
707
		case 's':
708
			if (annotate_browser__toggle_source(browser))
709 710
				ui_helpline__puts(help);
			continue;
711
		case 'o':
712
			annotate_browser__opts.use_offset = !annotate_browser__opts.use_offset;
713
			annotate_browser__update_addr_width(browser);
714
			continue;
715
		case 'j':
716
			annotate_browser__opts.jump_arrows = !annotate_browser__opts.jump_arrows;
717
			continue;
718
		case 'J':
719
			annotate_browser__opts.show_nr_jumps = !annotate_browser__opts.show_nr_jumps;
720
			annotate_browser__update_addr_width(browser);
721
			continue;
722
		case '/':
723
			if (annotate_browser__search(browser, delay_secs)) {
724 725 726 727 728
show_help:
				ui_helpline__puts(help);
			}
			continue;
		case 'n':
729 730 731
			if (browser->searching_backwards ?
			    annotate_browser__continue_search_reverse(browser, delay_secs) :
			    annotate_browser__continue_search(browser, delay_secs))
732 733 734
				goto show_help;
			continue;
		case '?':
735
			if (annotate_browser__search_reverse(browser, delay_secs))
736 737
				goto show_help;
			continue;
738 739 740 741
		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",
742 743 744 745 746
					   seq++, browser->b.nr_entries,
					   browser->b.height,
					   browser->b.index,
					   browser->b.top_idx,
					   browser->nr_asm_entries);
747 748
		}
			continue;
749 750
		case K_ENTER:
		case K_RIGHT:
751
			if (browser->selection == NULL)
752
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
753
			else if (browser->selection->offset == -1)
754
				ui_helpline__puts("Actions are only available for assembly lines.");
755 756
			else if (!browser->selection->ins) {
				if (strcmp(browser->selection->name, "retq"))
757 758
					goto show_sup_ins;
				goto out;
759
			} else if (!(annotate_browser__jump(browser) ||
760
				     annotate_browser__callq(browser, evsel, hbt))) {
761 762 763
show_sup_ins:
				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
			}
764
			continue;
765 766
		case K_LEFT:
		case K_ESC:
767 768
		case 'q':
		case CTRL('c'):
769
			goto out;
770 771
		default:
			continue;
772
		}
773 774

		if (nd != NULL)
775
			annotate_browser__set_rb_top(browser, nd);
776 777
	}
out:
778
	ui_browser__hide(&browser->b);
779
	return key;
780 781
}

782
int hist_entry__tui_annotate(struct hist_entry *he, struct perf_evsel *evsel,
783
			     struct hist_browser_timer *hbt)
784
{
785
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evsel, hbt);
786 787
}

788 789 790 791
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;
792 793 794 795 796 797
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;

	/* PLT symbols contain external offsets */
	if (strstr(sym->name, "@plt"))
		return;
798 799 800 801 802

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

803
		if (!disasm_line__is_valid_jump(dl, sym))
804 805
			continue;

806
		dlt = browser->offsets[dl->ops.target.offset];
807 808 809 810 811 812 813
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

814
		bdlt = disasm_line__browser(dlt);
815 816 817 818
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
819 820 821 822
	}
		
}

823 824 825 826 827 828 829 830 831
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

832 833
int symbol__tui_annotate(struct symbol *sym, struct map *map,
			 struct perf_evsel *evsel,
834
			 struct hist_browser_timer *hbt)
835
{
836
	struct disasm_line *pos, *n;
837
	struct annotation *notes;
838
	size_t size;
839 840 841 842
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
843 844
	struct annotate_browser browser = {
		.b = {
845
			.refresh = annotate_browser__refresh,
846 847
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
848
			.filter  = disasm_line__filter,
849
			.priv	 = &ms,
850
			.use_navkeypressed = true,
851
		},
852
	};
853
	int ret = -1;
854

855
	if (sym == NULL)
856 857
		return -1;

858 859
	size = symbol__size(sym);

860
	if (map->dso->annotate_warned)
861 862
		return -1;

863 864 865 866 867 868
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

869
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
870
		ui__error("%s", ui_helpline__last_msg);
871
		goto out_free_offsets;
872 873 874 875
	}

	ui_helpline__push("Press <- or ESC to exit");

876
	notes = symbol__annotation(sym);
877
	browser.start = map__rip_2objdump(map, sym->start);
878

879
	list_for_each_entry(pos, &notes->src->source, node) {
880
		struct browser_disasm_line *bpos;
881
		size_t line_len = strlen(pos->line);
882

883 884
		if (browser.b.width < line_len)
			browser.b.width = line_len;
885 886
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
887
		if (pos->offset != -1) {
888
			bpos->idx_asm = browser.nr_asm_entries++;
889 890 891 892 893 894 895 896 897
			/*
			 * 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
 			 */
			if (pos->offset < (s64)size)
				browser.offsets[pos->offset] = pos;
898
		} else
899
			bpos->idx_asm = -1;
900 901
	}

902 903
	annotate_browser__mark_jump_targets(&browser, size);

904
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
905
	browser.max_addr_width = hex_width(sym->end);
906
	browser.jumps_width = width_jumps(browser.max_jump_sources);
907
	browser.b.nr_entries = browser.nr_entries;
908
	browser.b.entries = &notes->src->source,
909
	browser.b.width += 18; /* Percentage */
910 911 912 913 914 915

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

	annotate_browser__update_addr_width(&browser);

916
	ret = annotate_browser__run(&browser, evsel, hbt);
917
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
918
		list_del(&pos->node);
919
		disasm_line__free(pos);
920
	}
921 922 923

out_free_offsets:
	free(browser.offsets);
924 925
	return ret;
}
926 927 928

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

930 931 932
/*
 * Keep the entries sorted, they are bsearch'ed
 */
933
static struct annotate_config {
934 935 936 937 938 939 940 941 942 943 944 945 946
	const char *name;
	bool *value;
} annotate__configs[] = {
	ANNOTATE_CFG(hide_src_code),
	ANNOTATE_CFG(jump_arrows),
	ANNOTATE_CFG(show_nr_jumps),
	ANNOTATE_CFG(use_offset),
};

#undef ANNOTATE_CFG

static int annotate_config__cmp(const void *name, const void *cfgp)
{
947
	const struct annotate_config *cfg = cfgp;
948 949 950 951

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

952 953
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
954
{
955
	struct annotate_config *cfg;
956 957 958 959 960 961 962
	const char *name;

	if (prefixcmp(var, "annotate.") != 0)
		return 0;

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
963
		      sizeof(struct annotate_config), annotate_config__cmp);
964 965 966 967 968 969 970 971 972 973 974 975

	if (cfg == NULL)
		return -1;

	*cfg->value = perf_config_bool(name, value);
	return 0;
}

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