annotate.c 16.4 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
struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
17
	struct rb_node	  *curr_hot;
18
	struct disasm_line	  *selection;
19
	u64		    start;
20 21 22
	int		    nr_asm_entries;
	int		    nr_entries;
	bool		    hide_src_code;
23
	bool		    use_offset;
24 25
	bool		    searching_backwards;
	char		    search_bf[128];
26 27
};

28
struct disasm_line_rb_node {
29 30 31
	struct rb_node	rb_node;
	double		percent;
	u32		idx;
32
	int		idx_asm;
33 34
};

35
static inline struct disasm_line_rb_node *disasm_line__rb(struct disasm_line *dl)
36
{
37
	return (struct disasm_line_rb_node *)(dl + 1);
38 39
}

40
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
41 42 43 44
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);

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

	return false;
}

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

62 63 64 65
	if (dl->offset != -1) {
		struct disasm_line_rb_node *dlrb = disasm_line__rb(dl);
		ui_browser__set_percent_color(self, dlrb->percent, current_entry);
		slsmg_printf(" %7.2f ", dlrb->percent);
66
	} else {
67
		ui_browser__set_percent_color(self, 0, current_entry);
68 69 70 71 72
		slsmg_write_nstring(" ", 9);
	}

	SLsmg_write_char(':');
	slsmg_write_nstring(" ", 8);
73 74 75 76 77

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

78
	if (dl->offset != -1 && change_color)
79
		ui_browser__set_color(self, HE_COLORSET_CODE);
80

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

90 91 92 93
		if (!ab->use_offset)
			addr += ab->start;

		printed = scnprintf(bf, sizeof(bf), " %" PRIx64 ":", addr);
94 95 96 97 98
		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);
99
		slsmg_write_nstring(dl->line, width - 18 - printed);
100
	}
101

102
	if (current_entry)
103
		ab->selection = dl;
104 105
}

106
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
107 108 109
{
	double percent = 0.0;

110
	if (dl->offset != -1) {
111
		int len = sym->end - sym->start;
112
		unsigned int hits = 0;
113
		struct annotation *notes = symbol__annotation(sym);
114
		struct source_line *src_line = notes->src->lines;
115
		struct sym_hist *h = annotation__histogram(notes, evidx);
116 117
		s64 offset = dl->offset;
		struct disasm_line *next;
118

119
		next = disasm__get_next_ip_line(&notes->src->source, dl);
120 121
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
122 123
			if (src_line) {
				percent += src_line[offset].percent;
124
			} else
125
				hits += h->addr[offset];
126 127 128

			++offset;
		}
129 130 131 132 133
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
134 135 136
			percent = 100.0 * hits / h->sum;
	}

137 138 139
	return percent;
}

140
static void disasm_rb_tree__insert(struct rb_root *root, struct disasm_line_rb_node *dlrb)
141
{
142
	struct rb_node **p = &root->rb_node;
143
	struct rb_node *parent = NULL;
144
	struct disasm_line_rb_node *l;
145 146 147

	while (*p != NULL) {
		parent = *p;
148 149
		l = rb_entry(parent, struct disasm_line_rb_node, rb_node);
		if (dlrb->percent < l->percent)
150 151 152 153
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
154 155
	rb_link_node(&dlrb->rb_node, parent, p);
	rb_insert_color(&dlrb->rb_node, root);
156 157
}

158
static void annotate_browser__set_top(struct annotate_browser *self,
159
				      struct disasm_line *pos, u32 idx)
160 161 162 163 164
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
165
	self->b.top_idx = self->b.index = idx;
166 167

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

170
		if (disasm_line__filter(&self->b, &pos->node))
171 172
			continue;

173 174 175 176 177
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
178
	self->b.navkeypressed = true;
179 180 181 182 183
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
184 185
	struct disasm_line_rb_node *rbpos;
	struct disasm_line *pos;
186

187 188
	rbpos = rb_entry(nd, struct disasm_line_rb_node, rb_node);
	pos = ((struct disasm_line *)rbpos) - 1;
189 190
	annotate_browser__set_top(browser, pos, rbpos->idx);
	browser->curr_hot = nd;
191 192
}

193 194
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
195
{
196 197
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
198
	struct annotation *notes = symbol__annotation(sym);
199
	struct disasm_line *pos;
200 201 202 203 204 205

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
206 207
		struct disasm_line_rb_node *rbpos = disasm_line__rb(pos);
		rbpos->percent = disasm_line__calc_percent(pos, sym, evidx);
208 209 210 211
		if (rbpos->percent < 0.01) {
			RB_CLEAR_NODE(&rbpos->rb_node);
			continue;
		}
212
		disasm_rb_tree__insert(&browser->entries, rbpos);
213 214 215 216 217 218
	}
	pthread_mutex_unlock(&notes->lock);

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

219 220
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
221 222
	struct disasm_line *dl;
	struct disasm_line_rb_node *dlrb;
223 224 225
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
226 227
	dl = list_entry(browser->b.top, struct disasm_line, node);
	dlrb = disasm_line__rb(dl);
228 229

	if (browser->hide_src_code) {
230 231
		if (dlrb->idx_asm < offset)
			offset = dlrb->idx;
232 233 234 235

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
236 237
		browser->b.top_idx = dlrb->idx - offset;
		browser->b.index = dlrb->idx;
238
	} else {
239
		if (dlrb->idx_asm < 0) {
240 241 242 243 244
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

245 246
		if (dlrb->idx_asm < offset)
			offset = dlrb->idx_asm;
247 248 249 250

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
251 252
		browser->b.top_idx = dlrb->idx_asm - offset;
		browser->b.index = dlrb->idx_asm;
253 254 255 256 257
	}

	return true;
}

258 259 260 261 262
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;
263
	struct disasm_line *dl = browser->selection;
264 265 266 267 268
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

269
	if (strcmp(dl->name, "callq"))
270 271
		return false;

272
	ip = strtoull(dl->operands, NULL, 16);
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
	ip = ms->map->map_ip(ms->map, ip);
	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;
}

296 297 298
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
299 300 301 302
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
303
	struct disasm_line *pos;
304 305 306 307 308

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
309
		if (!disasm_line__filter(&browser->b, &pos->node))
310 311 312 313 314 315 316 317
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
318 319
	const char *jumps[] = { "je", "jne", "ja", "jmpq", "js", "jmp", NULL };
	struct disasm_line *dl = browser->selection;
320
	s64 idx, offset;
321
	char *s;
322 323
	int i = 0;

324 325
	while (jumps[i] && strcmp(dl->name, jumps[i]))
		++i;
326

327
	if (jumps[i] == NULL)
328 329
		return false;

330
	s = strchr(dl->operands, '+');
331 332 333 334 335 336
	if (s++ == NULL) {
		ui_helpline__puts("Invallid jump instruction.");
		return true;
	}

	offset = strtoll(s, NULL, 16);
337 338
	dl = annotate_browser__find_offset(browser, offset, &idx);
	if (dl == NULL) {
339 340 341 342
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

343
	annotate_browser__set_top(browser, dl, idx);
344 345 346 347
	
	return true;
}

348 349 350
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
351 352 353 354
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
355
	struct disasm_line *pos = browser->selection;
356 357 358

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
359
		if (disasm_line__filter(&browser->b, &pos->node))
360 361 362 363 364 365 366 367 368 369 370 371 372
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
373
	struct disasm_line *dl;
374 375
	s64 idx;

376 377
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
378 379 380 381
		ui_helpline__puts("String not found!");
		return false;
	}

382
	annotate_browser__set_top(browser, dl, idx);
383 384 385 386
	browser->searching_backwards = false;
	return true;
}

387 388 389
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
390 391 392 393
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
394
	struct disasm_line *pos = browser->selection;
395 396 397

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
398
		if (disasm_line__filter(&browser->b, &pos->node))
399 400 401 402 403 404 405 406 407 408 409 410 411
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
412
	struct disasm_line *dl;
413 414
	s64 idx;

415 416
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
417 418 419 420
		ui_helpline__puts("String not found!");
		return false;
	}

421
	annotate_browser__set_top(browser, dl, idx);
422 423 424 425 426 427 428 429 430 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
	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);
}

474
static int annotate_browser__run(struct annotate_browser *self, int evidx,
475
				 void(*timer)(void *arg),
476
				 void *arg, int delay_secs)
477 478
{
	struct rb_node *nd = NULL;
479 480
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
481 482
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Go to hottest line, ->/ENTER: Line action, "
483
			   "O: Toggle offset view, "
484
			   "S: Toggle source code view";
485
	int key;
486

487
	if (ui_browser__show(&self->b, sym->name, help) < 0)
488
		return -1;
489 490 491

	annotate_browser__calc_percent(self, evidx);

492
	if (self->curr_hot) {
493
		annotate_browser__set_rb_top(self, self->curr_hot);
494 495
		self->b.navkeypressed = false;
	}
496 497

	nd = self->curr_hot;
498

499
	while (1) {
500
		key = ui_browser__run(&self->b, delay_secs);
501

502
		if (delay_secs != 0) {
503 504 505 506 507 508 509 510 511 512
			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;
		}

513
		switch (key) {
514
		case K_TIMER:
515 516 517 518
			if (timer != NULL)
				timer(arg);

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

		if (nd != NULL)
586
			annotate_browser__set_rb_top(self, nd);
587 588
	}
out:
589
	ui_browser__hide(&self->b);
590
	return key;
591 592
}

593
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
594
			     void(*timer)(void *arg), void *arg, int delay_secs)
595
{
596
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
597
				    timer, arg, delay_secs);
598 599
}

600
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
601
			 void(*timer)(void *arg), void *arg,
602
			 int delay_secs)
603
{
604
	struct disasm_line *pos, *n;
605
	struct annotation *notes;
606 607 608 609
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
610 611 612 613 614
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
615
			.filter  = disasm_line__filter,
616
			.priv	 = &ms,
617
			.use_navkeypressed = true,
618
		},
619 620 621
	};
	int ret;

622
	if (sym == NULL)
623 624
		return -1;

625
	if (map->dso->annotate_warned)
626 627
		return -1;

628
	if (symbol__annotate(sym, map, sizeof(struct disasm_line_rb_node)) < 0) {
629
		ui__error("%s", ui_helpline__last_msg);
630 631 632 633 634
		return -1;
	}

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

635
	notes = symbol__annotation(sym);
636
	browser.start = map__rip_2objdump(map, sym->start);
637

638
	list_for_each_entry(pos, &notes->src->source, node) {
639
		struct disasm_line_rb_node *rbpos;
640
		size_t line_len = strlen(pos->line);
641

642 643
		if (browser.b.width < line_len)
			browser.b.width = line_len;
644
		rbpos = disasm_line__rb(pos);
645 646 647 648 649
		rbpos->idx = browser.nr_entries++;
		if (pos->offset != -1)
			rbpos->idx_asm = browser.nr_asm_entries++;
		else
			rbpos->idx_asm = -1;
650 651
	}

652
	browser.b.nr_entries = browser.nr_entries;
653
	browser.b.entries = &notes->src->source,
654
	browser.b.width += 18; /* Percentage */
655
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
656
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
657
		list_del(&pos->node);
658
		disasm_line__free(pos);
659 660 661
	}
	return ret;
}