annotate.c 24.5 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 <pthread.h>
12
#include <newt.h>
13

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

22 23 24 25 26 27 28 29 30 31
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,
};

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

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

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

	return false;
}

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

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

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

107
	SLsmg_write_char(' ');
108 109

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

113
	if (!*dl->line)
114
		slsmg_write_nstring(" ", width - 7);
115 116 117 118 119 120
	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 {
121
		u64 addr = dl->offset;
122
		int color = -1;
123

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

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

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

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

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

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

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

185
static void annotate_browser__draw_current_jump(struct ui_browser *browser)
186 187
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
188 189
	struct disasm_line *cursor = ab->selection, *target;
	struct browser_disasm_line *btarget, *bcursor;
190
	unsigned int from, to;
191

192
	if (!cursor || !cursor->ins || !ins__is_jump(cursor->ins) ||
193 194
	    !disasm_line__has_offset(cursor))
		return;
195

196 197 198
	target = ab->offsets[cursor->ops.target.offset];
	if (!target)
		return;
199

200 201
	bcursor = disasm_line__browser(cursor);
	btarget = disasm_line__browser(target);
202

203
	if (annotate_browser__opts.hide_src_code) {
204
		from = bcursor->idx_asm;
205 206
		to = btarget->idx_asm;
	} else {
207
		from = (u64)bcursor->idx;
208 209 210 211
		to = (u64)btarget->idx;
	}

	ui_browser__set_color(browser, HE_COLORSET_CODE);
212
	__ui_browser__line_arrow(browser, 9 + ab->addr_width, from, to);
213 214 215 216 217 218
}

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

219
	if (annotate_browser__opts.jump_arrows)
220
		annotate_browser__draw_current_jump(browser);
221

222 223
	ui_browser__set_color(browser, HE_COLORSET_NORMAL);
	__ui_browser__vline(browser, 7, 0, browser->height - 1);
224 225 226
	return ret;
}

227
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
228 229 230
{
	double percent = 0.0;

231
	if (dl->offset != -1) {
232
		int len = sym->end - sym->start;
233
		unsigned int hits = 0;
234
		struct annotation *notes = symbol__annotation(sym);
235
		struct source_line *src_line = notes->src->lines;
236
		struct sym_hist *h = annotation__histogram(notes, evidx);
237 238
		s64 offset = dl->offset;
		struct disasm_line *next;
239

240
		next = disasm__get_next_ip_line(&notes->src->source, dl);
241 242
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
243 244
			if (src_line) {
				percent += src_line[offset].percent;
245
			} else
246
				hits += h->addr[offset];
247 248 249

			++offset;
		}
250 251 252 253 254
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
255 256 257
			percent = 100.0 * hits / h->sum;
	}

258 259 260
	return percent;
}

261
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
262
{
263
	struct rb_node **p = &root->rb_node;
264
	struct rb_node *parent = NULL;
265
	struct browser_disasm_line *l;
266 267 268

	while (*p != NULL) {
		parent = *p;
269 270
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
271 272 273 274
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
275 276
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
277 278
}

279
static void annotate_browser__set_top(struct annotate_browser *browser,
280
				      struct disasm_line *pos, u32 idx)
281 282 283
{
	unsigned back;

284 285 286
	ui_browser__refresh_dimensions(&browser->b);
	back = browser->b.height / 2;
	browser->b.top_idx = browser->b.index = idx;
287

288
	while (browser->b.top_idx != 0 && back != 0) {
289
		pos = list_entry(pos->node.prev, struct disasm_line, node);
290

291
		if (disasm_line__filter(&browser->b, &pos->node))
292 293
			continue;

294
		--browser->b.top_idx;
295 296 297
		--back;
	}

298 299
	browser->b.top = pos;
	browser->b.navkeypressed = true;
300 301 302 303 304
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
305
	struct browser_disasm_line *bpos;
306
	struct disasm_line *pos;
307
	u32 idx;
308

309 310
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
311
	idx = bpos->idx;
312
	if (annotate_browser__opts.hide_src_code)
313 314
		idx = bpos->idx_asm;
	annotate_browser__set_top(browser, pos, idx);
315
	browser->curr_hot = nd;
316 317
}

318 319
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
320
{
321 322
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
323
	struct annotation *notes = symbol__annotation(sym);
324
	struct disasm_line *pos;
325 326 327 328 329 330

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
331 332 333 334
		struct browser_disasm_line *bpos = disasm_line__browser(pos);
		bpos->percent = disasm_line__calc_percent(pos, sym, evidx);
		if (bpos->percent < 0.01) {
			RB_CLEAR_NODE(&bpos->rb_node);
335 336
			continue;
		}
337
		disasm_rb_tree__insert(&browser->entries, bpos);
338 339 340 341 342 343
	}
	pthread_mutex_unlock(&notes->lock);

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

344 345
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
346
	struct disasm_line *dl;
347
	struct browser_disasm_line *bdl;
348 349 350
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
351
	dl = list_entry(browser->b.top, struct disasm_line, node);
352
	bdl = disasm_line__browser(dl);
353

354
	if (annotate_browser__opts.hide_src_code) {
355 356
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
357 358

		browser->b.nr_entries = browser->nr_entries;
359
		annotate_browser__opts.hide_src_code = false;
360
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
361 362
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
363
	} else {
364
		if (bdl->idx_asm < 0) {
365 366 367 368 369
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

370 371
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
372 373

		browser->b.nr_entries = browser->nr_asm_entries;
374
		annotate_browser__opts.hide_src_code = true;
375
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
376 377
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
378 379 380 381 382
	}

	return true;
}

383 384 385 386 387 388
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;
}

389 390 391 392 393
static bool annotate_browser__callq(struct annotate_browser *browser,
				    int evidx, void (*timer)(void *arg),
				    void *arg, int delay_secs)
{
	struct map_symbol *ms = browser->b.priv;
394
	struct disasm_line *dl = browser->selection;
395 396 397 398 399
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

400
	if (!ins__is_call(dl->ins))
401 402
		return false;

403
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425
	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);
	symbol__tui_annotate(target, ms->map, evidx, timer, arg, delay_secs);
	ui_browser__show_title(&browser->b, sym->name);
	return true;
}

426 427 428
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
429 430 431 432
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
433
	struct disasm_line *pos;
434 435 436 437 438

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
439
		if (!disasm_line__filter(&browser->b, &pos->node))
440 441 442 443 444 445 446 447
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
448
	struct disasm_line *dl = browser->selection;
449
	s64 idx;
450

451
	if (!ins__is_jump(dl->ins))
452 453
		return false;

454
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
455
	if (dl == NULL) {
456 457 458 459
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

460
	annotate_browser__set_top(browser, dl, idx);
461 462 463 464
	
	return true;
}

465 466 467
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
468 469 470 471
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
472
	struct disasm_line *pos = browser->selection;
473 474 475

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
476
		if (disasm_line__filter(&browser->b, &pos->node))
477 478 479 480 481 482 483 484 485 486 487 488 489
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
490
	struct disasm_line *dl;
491 492
	s64 idx;

493 494
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
495 496 497 498
		ui_helpline__puts("String not found!");
		return false;
	}

499
	annotate_browser__set_top(browser, dl, idx);
500 501 502 503
	browser->searching_backwards = false;
	return true;
}

504 505 506
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
507 508 509 510
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
511
	struct disasm_line *pos = browser->selection;
512 513 514

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
515
		if (disasm_line__filter(&browser->b, &pos->node))
516 517 518 519 520 521 522 523 524 525 526 527 528
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
529
	struct disasm_line *dl;
530 531
	s64 idx;

532 533
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
534 535 536 537
		ui_helpline__puts("String not found!");
		return false;
	}

538
	annotate_browser__set_top(browser, dl, idx);
539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 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
	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);
}

591 592 593 594 595 596 597 598 599 600 601 602 603
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;
}

604
static int annotate_browser__run(struct annotate_browser *browser, int evidx,
605
				 void(*timer)(void *arg),
606
				 void *arg, int delay_secs)
607 608
{
	struct rb_node *nd = NULL;
609
	struct map_symbol *ms = browser->b.priv;
610
	struct symbol *sym = ms->sym;
611
	const char *help = "Press 'h' for help on key bindings";
612
	int key;
613

614
	if (ui_browser__show(&browser->b, sym->name, help) < 0)
615
		return -1;
616

617
	annotate_browser__calc_percent(browser, evidx);
618

619 620 621
	if (browser->curr_hot) {
		annotate_browser__set_rb_top(browser, browser->curr_hot);
		browser->b.navkeypressed = false;
622
	}
623

624
	nd = browser->curr_hot;
625

626
	while (1) {
627
		key = ui_browser__run(&browser->b, delay_secs);
628

629
		if (delay_secs != 0) {
630
			annotate_browser__calc_percent(browser, evidx);
631 632 633 634 635 636 637 638 639
			/*
			 * 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;
		}

640
		switch (key) {
641
		case K_TIMER:
642 643 644 645
			if (timer != NULL)
				timer(arg);

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

		if (nd != NULL)
752
			annotate_browser__set_rb_top(browser, nd);
753 754
	}
out:
755
	ui_browser__hide(&browser->b);
756
	return key;
757 758
}

759
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
760
			     void(*timer)(void *arg), void *arg, int delay_secs)
761
{
762
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
763
				    timer, arg, delay_secs);
764 765
}

766 767 768 769 770 771 772 773 774
static void annotate_browser__mark_jump_targets(struct annotate_browser *browser,
						size_t size)
{
	u64 offset;

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

775 776
		if (!dl || !dl->ins || !ins__is_jump(dl->ins) ||
		    !disasm_line__has_offset(dl))
777 778
			continue;

779
		if (dl->ops.target.offset >= size) {
780 781
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
782
				  size, dl->ops.target.offset);
783 784 785
			continue;
		}

786
		dlt = browser->offsets[dl->ops.target.offset];
787 788 789 790 791 792 793
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

794
		bdlt = disasm_line__browser(dlt);
795 796 797 798
		if (++bdlt->jump_sources > browser->max_jump_sources)
			browser->max_jump_sources = bdlt->jump_sources;

		++browser->nr_jumps;
799 800 801 802
	}
		
}

803 804 805 806 807 808 809 810 811
static inline int width_jumps(int n)
{
	if (n >= 100)
		return 5;
	if (n / 10)
		return 2;
	return 1;
}

812
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
813
			 void(*timer)(void *arg), void *arg,
814
			 int delay_secs)
815
{
816
	struct disasm_line *pos, *n;
817
	struct annotation *notes;
818
	size_t size;
819 820 821 822
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
823 824
	struct annotate_browser browser = {
		.b = {
825
			.refresh = annotate_browser__refresh,
826 827
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
828
			.filter  = disasm_line__filter,
829
			.priv	 = &ms,
830
			.use_navkeypressed = true,
831
		},
832
	};
833
	int ret = -1;
834

835
	if (sym == NULL)
836 837
		return -1;

838 839
	size = symbol__size(sym);

840
	if (map->dso->annotate_warned)
841 842
		return -1;

843 844 845 846 847 848
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

849
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
850
		ui__error("%s", ui_helpline__last_msg);
851
		goto out_free_offsets;
852 853 854 855
	}

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

856
	notes = symbol__annotation(sym);
857
	browser.start = map__rip_2objdump(map, sym->start);
858

859
	list_for_each_entry(pos, &notes->src->source, node) {
860
		struct browser_disasm_line *bpos;
861
		size_t line_len = strlen(pos->line);
862

863 864
		if (browser.b.width < line_len)
			browser.b.width = line_len;
865 866
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
867
		if (pos->offset != -1) {
868
			bpos->idx_asm = browser.nr_asm_entries++;
869 870 871 872 873 874 875 876 877
			/*
			 * 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;
878
		} else
879
			bpos->idx_asm = -1;
880 881
	}

882 883
	annotate_browser__mark_jump_targets(&browser, size);

884
	browser.addr_width = browser.target_width = browser.min_addr_width = hex_width(size);
885
	browser.max_addr_width = hex_width(sym->end);
886
	browser.jumps_width = width_jumps(browser.max_jump_sources);
887
	browser.b.nr_entries = browser.nr_entries;
888
	browser.b.entries = &notes->src->source,
889
	browser.b.width += 18; /* Percentage */
890 891 892 893 894 895

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

	annotate_browser__update_addr_width(&browser);

896
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
897
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
898
		list_del(&pos->node);
899
		disasm_line__free(pos);
900
	}
901 902 903

out_free_offsets:
	free(browser.offsets);
904 905
	return ret;
}
906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931

#define ANNOTATE_CFG(n) \
	{ .name = #n, .value = &annotate_browser__opts.n, }
	
/*
 * Keep the entries sorted, they are bsearch'ed
 */
static struct annotate__config {
	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)
{
	const struct annotate__config *cfg = cfgp;

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

932 933
static int annotate__config(const char *var, const char *value,
			    void *data __maybe_unused)
934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955
{
	struct annotate__config *cfg;
	const char *name;

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

	name = var + 9;
	cfg = bsearch(name, annotate__configs, ARRAY_SIZE(annotate__configs),
		      sizeof(struct annotate__config), annotate_config__cmp);

	if (cfg == NULL)
		return -1;

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

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