annotate.c 23.3 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
static bool disasm_line__filter(struct ui_browser *browser __used, void *entry)
58
{
59
	if (annotate_browser__opts.hide_src_code) {
60 61
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
62 63 64 65 66
	}

	return false;
}

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

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

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

106
	SLsmg_write_char(' ');
107 108 109 110 111

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

257 258 259
	return percent;
}

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

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

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

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

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

290
		if (disasm_line__filter(&self->b, &pos->node))
291 292
			continue;

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
330 331 332 333
		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);
334 335
			continue;
		}
336
		disasm_rb_tree__insert(&browser->entries, bpos);
337 338 339 340 341 342
	}
	pthread_mutex_unlock(&notes->lock);

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

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

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

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

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

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

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

	return true;
}

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

388 389 390 391 392
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;
393
	struct disasm_line *dl = browser->selection;
394 395 396 397 398
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

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

402
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424
	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;
}

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

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

	return NULL;
}

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

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

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

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

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

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

		++*idx;

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

	return NULL;
}

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

537
	annotate_browser__set_top(browser, dl, idx);
538 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
	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);
}

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

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

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

	annotate_browser__calc_percent(self, evidx);

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

	nd = self->curr_hot;
624

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

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

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

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

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

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

765 766 767 768 769 770 771 772 773
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;

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

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

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

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

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

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

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

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

837
	if (map->dso->annotate_warned)
838 839
		return -1;

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

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

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

853
	notes = symbol__annotation(sym);
854
	browser.start = map__rip_2objdump(map, sym->start);
855

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

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

879 880
	annotate_browser__mark_jump_targets(&browser, size);

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

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

	annotate_browser__update_addr_width(&browser);

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

out_free_offsets:
	free(browser.offsets);
901 902
	return ret;
}