annotate.c 17.6 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 74 75
		slsmg_write_nstring(" ", 9);
	}

	SLsmg_write_char(':');
	slsmg_write_nstring(" ", 8);
76 77 78 79 80

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

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

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

93 94 95
		if (!ab->use_offset)
			addr += ab->start;

96 97 98 99 100 101 102 103 104 105 106
		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, " ");
			}
		}
107

108 109 110 111 112
		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);
113 114 115 116 117 118 119 120 121 122
		if (dl->ins && dl->ins->ops->scnprintf) {
			dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf),
						!ab->use_offset ? dl->operands : NULL,
						dl->target);
			line = bf;
			slsmg_write_nstring(" ", 7);
			printed += 7;
		}

		slsmg_write_nstring(line, width - 18 - printed);
123
	}
124

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

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

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

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

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

160 161 162
	return percent;
}

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

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

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

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

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

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

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

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

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

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

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

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

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

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

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

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

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

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

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

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

	return true;
}

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

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

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

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

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

	return NULL;
}

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

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

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

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

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

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

		++*idx;

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

	return NULL;
}

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

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

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

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

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

		--*idx;

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

	return NULL;
}

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

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

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

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

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

	annotate_browser__calc_percent(self, evidx);

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

	nd = self->curr_hot;
507

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

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

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

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

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

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

610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635
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;

		if (dl->target >= size) {
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
				  size, dl->target);
			continue;
		}

		dlt = browser->offsets[dl->target];
		bdlt = disasm_line__browser(dlt);
		bdlt->jump_target = true;
	}
		
}

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

659
	if (sym == NULL)
660 661
		return -1;

662
	if (map->dso->annotate_warned)
663 664
		return -1;

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

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

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

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

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

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

696 697
	annotate_browser__mark_jump_targets(&browser, size);

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

out_free_offsets:
	free(browser.offsets);
710 711
	return ret;
}