annotate.c 17.7 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 19 20 21
struct browser_disasm_line {
	struct rb_node	rb_node;
	double		percent;
	u32		idx;
	int		idx_asm;
	bool		jump_target;
};

22 23 24
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
25
	struct rb_node	  *curr_hot;
26
	struct disasm_line	  *selection;
27
	struct disasm_line  **offsets;
28
	u64		    start;
29 30 31
	int		    nr_asm_entries;
	int		    nr_entries;
	bool		    hide_src_code;
32
	bool		    use_offset;
33
	bool		    searching_backwards;
34
	u8		    offset_width;
35
	char		    search_bf[128];
36 37
};

38
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
39
{
40
	return (struct browser_disasm_line *)(dl + 1);
41 42
}

43
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
44 45 46 47
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);

	if (ab->hide_src_code) {
48 49
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
50 51 52 53 54
	}

	return false;
}

55 56
static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
57
	struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
58
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
59
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
60
	bool current_entry = ui_browser__is_current_entry(self, row);
61 62 63
	bool change_color = (!ab->hide_src_code &&
			     (!current_entry || (self->use_navkeypressed &&
					         !self->navkeypressed)));
64 65
	int width = self->width;

66
	if (dl->offset != -1) {
67 68
		ui_browser__set_percent_color(self, bdl->percent, current_entry);
		slsmg_printf(" %7.2f ", bdl->percent);
69
	} else {
70
		ui_browser__set_percent_color(self, 0, current_entry);
71 72 73
		slsmg_write_nstring(" ", 9);
	}

74 75 76 77
	SLsmg_set_char_set(1);
	SLsmg_write_char(SLSMG_VLINE_CHAR);
	SLsmg_set_char_set(0);
	SLsmg_write_char(' ');
78 79 80 81 82

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

83
	if (dl->offset != -1 && change_color)
84
		ui_browser__set_color(self, HE_COLORSET_CODE);
85

86
	if (!*dl->line)
87
		slsmg_write_nstring(" ", width - 10);
88
	else if (dl->offset == -1)
89
		slsmg_write_nstring(dl->line, width - 10);
90
	else {
91
		char bf[256];
92
		u64 addr = dl->offset;
93
		int printed, color = -1;
94

95 96 97
		if (!ab->use_offset)
			addr += ab->start;

98 99 100 101 102 103 104 105 106 107 108
		if (!ab->use_offset) {
			printed = scnprintf(bf, sizeof(bf), "%" PRIx64 ":", addr);
		} else {
			if (bdl->jump_target) {
				printed = scnprintf(bf, sizeof(bf), "%*" PRIx64 ":",
						    ab->offset_width, addr);
			} else {
				printed = scnprintf(bf, sizeof(bf), "%*s ",
						    ab->offset_width, " ");
			}
		}
109

110 111 112 113 114
		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);
115
		if (dl->ins && dl->ins->ops->scnprintf) {
116 117
			dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf), &dl->ops,
						!ab->use_offset);
118 119 120
			slsmg_write_nstring(" ", 2);
			printed += 2;
		} else
121
			scnprintf(bf, sizeof(bf), "  %-6.6s %s", dl->name, dl->ops.raw);
122

123
		slsmg_write_nstring(bf, width - 10 - printed);
124
	}
125

126
	if (current_entry)
127
		ab->selection = dl;
128 129
}

130
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
131 132 133
{
	double percent = 0.0;

134
	if (dl->offset != -1) {
135
		int len = sym->end - sym->start;
136
		unsigned int hits = 0;
137
		struct annotation *notes = symbol__annotation(sym);
138
		struct source_line *src_line = notes->src->lines;
139
		struct sym_hist *h = annotation__histogram(notes, evidx);
140 141
		s64 offset = dl->offset;
		struct disasm_line *next;
142

143
		next = disasm__get_next_ip_line(&notes->src->source, dl);
144 145
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
146 147
			if (src_line) {
				percent += src_line[offset].percent;
148
			} else
149
				hits += h->addr[offset];
150 151 152

			++offset;
		}
153 154 155 156 157
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
158 159 160
			percent = 100.0 * hits / h->sum;
	}

161 162 163
	return percent;
}

164
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
165
{
166
	struct rb_node **p = &root->rb_node;
167
	struct rb_node *parent = NULL;
168
	struct browser_disasm_line *l;
169 170 171

	while (*p != NULL) {
		parent = *p;
172 173
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
174 175 176 177
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
178 179
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
180 181
}

182
static void annotate_browser__set_top(struct annotate_browser *self,
183
				      struct disasm_line *pos, u32 idx)
184 185 186 187 188
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
189
	self->b.top_idx = self->b.index = idx;
190 191

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

194
		if (disasm_line__filter(&self->b, &pos->node))
195 196
			continue;

197 198 199 200 201
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
202
	self->b.navkeypressed = true;
203 204 205 206 207
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
208
	struct browser_disasm_line *bpos;
209
	struct disasm_line *pos;
210

211 212 213
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
	annotate_browser__set_top(browser, pos, bpos->idx);
214
	browser->curr_hot = nd;
215 216
}

217 218
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
219
{
220 221
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
222
	struct annotation *notes = symbol__annotation(sym);
223
	struct disasm_line *pos;
224 225 226 227 228 229

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
230 231 232 233
		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);
234 235
			continue;
		}
236
		disasm_rb_tree__insert(&browser->entries, bpos);
237 238 239 240 241 242
	}
	pthread_mutex_unlock(&notes->lock);

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

243 244
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
245
	struct disasm_line *dl;
246
	struct browser_disasm_line *bdl;
247 248 249
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
250
	dl = list_entry(browser->b.top, struct disasm_line, node);
251
	bdl = disasm_line__browser(dl);
252 253

	if (browser->hide_src_code) {
254 255
		if (bdl->idx_asm < offset)
			offset = bdl->idx;
256 257 258 259

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
260 261
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
262
	} else {
263
		if (bdl->idx_asm < 0) {
264 265 266 267 268
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

269 270
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
271 272 273 274

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
275 276
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
277 278 279 280 281
	}

	return true;
}

282 283 284 285 286
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;
287
	struct disasm_line *dl = browser->selection;
288 289 290 291 292
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

293
	if (!ins__is_call(dl->ins))
294 295
		return false;

296
	ip = ms->map->map_ip(ms->map, dl->ops.target);
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
	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;
}

319 320 321
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
322 323 324 325
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
326
	struct disasm_line *pos;
327 328 329 330 331

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
332
		if (!disasm_line__filter(&browser->b, &pos->node))
333 334 335 336 337 338 339 340
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
341
	struct disasm_line *dl = browser->selection;
342
	s64 idx;
343

344
	if (!ins__is_jump(dl->ins))
345 346
		return false;

347
	dl = annotate_browser__find_offset(browser, dl->ops.target, &idx);
348
	if (dl == NULL) {
349 350 351 352
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

353
	annotate_browser__set_top(browser, dl, idx);
354 355 356 357
	
	return true;
}

358 359 360
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
361 362 363 364
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
365
	struct disasm_line *pos = browser->selection;
366 367 368

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
369
		if (disasm_line__filter(&browser->b, &pos->node))
370 371 372 373 374 375 376 377 378 379 380 381 382
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
383
	struct disasm_line *dl;
384 385
	s64 idx;

386 387
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
388 389 390 391
		ui_helpline__puts("String not found!");
		return false;
	}

392
	annotate_browser__set_top(browser, dl, idx);
393 394 395 396
	browser->searching_backwards = false;
	return true;
}

397 398 399
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
400 401 402 403
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
404
	struct disasm_line *pos = browser->selection;
405 406 407

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
408
		if (disasm_line__filter(&browser->b, &pos->node))
409 410 411 412 413 414 415 416 417 418 419 420 421
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
422
	struct disasm_line *dl;
423 424
	s64 idx;

425 426
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
427 428 429 430
		ui_helpline__puts("String not found!");
		return false;
	}

431
	annotate_browser__set_top(browser, dl, idx);
432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483
	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);
}

484
static int annotate_browser__run(struct annotate_browser *self, int evidx,
485
				 void(*timer)(void *arg),
486
				 void *arg, int delay_secs)
487 488
{
	struct rb_node *nd = NULL;
489 490
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
491 492
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Go to hottest line, ->/ENTER: Line action, "
493
			   "O: Toggle offset view, "
494
			   "S: Toggle source code view";
495
	int key;
496

497
	if (ui_browser__show(&self->b, sym->name, help) < 0)
498
		return -1;
499 500 501

	annotate_browser__calc_percent(self, evidx);

502
	if (self->curr_hot) {
503
		annotate_browser__set_rb_top(self, self->curr_hot);
504 505
		self->b.navkeypressed = false;
	}
506 507

	nd = self->curr_hot;
508

509
	while (1) {
510
		key = ui_browser__run(&self->b, delay_secs);
511

512
		if (delay_secs != 0) {
513 514 515 516 517 518 519 520 521 522
			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;
		}

523
		switch (key) {
524
		case K_TIMER:
525 526 527 528
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
529 530
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
531
		case K_TAB:
532 533 534 535 536 537
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
					nd = rb_last(&self->entries);
			} else
				nd = self->curr_hot;
538
			break;
539
		case K_UNTAB:
540 541 542 543 544 545 546 547
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
					nd = rb_first(&self->entries);
			else
				nd = self->curr_hot;
			break;
		case 'H':
548
		case 'h':
549
			nd = self->curr_hot;
550
			break;
551
		case 'S':
552
		case 's':
553 554 555
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
556 557 558 559
		case 'O':
		case 'o':
			self->use_offset = !self->use_offset;
			continue;
560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575
		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;
576 577
		case K_ENTER:
		case K_RIGHT:
578
			if (self->selection == NULL)
579
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
580
			else if (self->selection->offset == -1)
581
				ui_helpline__puts("Actions are only available for assembly lines.");
582 583
			else if (!self->selection->ins ||
				 !(annotate_browser__jump(self) ||
584 585
				   annotate_browser__callq(self, evidx, timer, arg, delay_secs)))
				ui_helpline__puts("Actions are only available for the 'callq' and jump instructions.");
586
			continue;
587 588
		case K_LEFT:
		case K_ESC:
589 590
		case 'q':
		case CTRL('c'):
591
			goto out;
592 593
		default:
			continue;
594
		}
595 596

		if (nd != NULL)
597
			annotate_browser__set_rb_top(self, nd);
598 599
	}
out:
600
	ui_browser__hide(&self->b);
601
	return key;
602 603
}

604
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
605
			     void(*timer)(void *arg), void *arg, int delay_secs)
606
{
607
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
608
				    timer, arg, delay_secs);
609 610
}

611 612 613 614 615 616 617 618 619 620 621 622
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;

		if (!dl || !dl->ins || !ins__is_jump(dl->ins))
			continue;

623
		if (dl->ops.target >= size) {
624 625
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
626
				  size, dl->ops.target);
627 628 629
			continue;
		}

630
		dlt = browser->offsets[dl->ops.target];
631 632 633 634 635 636
		bdlt = disasm_line__browser(dlt);
		bdlt->jump_target = true;
	}
		
}

637
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
638
			 void(*timer)(void *arg), void *arg,
639
			 int delay_secs)
640
{
641
	struct disasm_line *pos, *n;
642
	struct annotation *notes;
643
	const size_t size = symbol__size(sym);
644 645 646 647
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
648 649 650 651 652
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
653
			.filter  = disasm_line__filter,
654
			.priv	 = &ms,
655
			.use_navkeypressed = true,
656
		},
657
		.use_offset = true,
658
	};
659
	int ret = -1;
660

661
	if (sym == NULL)
662 663
		return -1;

664
	if (map->dso->annotate_warned)
665 666
		return -1;

667 668 669 670 671 672
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

673
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
674
		ui__error("%s", ui_helpline__last_msg);
675
		goto out_free_offsets;
676 677 678 679
	}

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

680
	notes = symbol__annotation(sym);
681
	browser.start = map__rip_2objdump(map, sym->start);
682

683
	list_for_each_entry(pos, &notes->src->source, node) {
684
		struct browser_disasm_line *bpos;
685
		size_t line_len = strlen(pos->line);
686

687 688
		if (browser.b.width < line_len)
			browser.b.width = line_len;
689 690
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
691
		if (pos->offset != -1) {
692
			bpos->idx_asm = browser.nr_asm_entries++;
693 694
			browser.offsets[pos->offset] = pos;
		} else
695
			bpos->idx_asm = -1;
696 697
	}

698 699
	annotate_browser__mark_jump_targets(&browser, size);

700
	browser.offset_width = hex_width(size);
701
	browser.b.nr_entries = browser.nr_entries;
702
	browser.b.entries = &notes->src->source,
703
	browser.b.width += 18; /* Percentage */
704
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
705
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
706
		list_del(&pos->node);
707
		disasm_line__free(pos);
708
	}
709 710 711

out_free_offsets:
	free(browser.offsets);
712 713
	return ret;
}