annotate.c 16.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
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 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
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;
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	char *s = strstr(browser->selection->line, "callq ");
	u64 ip;

	if (s == NULL)
		return false;

	s = strchr(s, ' ');
	if (s++ == NULL) {
		ui_helpline__puts("Invallid callq instruction.");
		return true;
	}

	ip = strtoull(s, NULL, 16);
	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;
}

302 303 304
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
305 306 307 308
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
309
	struct disasm_line *pos;
310 311 312 313 314

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
315
		if (!disasm_line__filter(&browser->b, &pos->node))
316 317 318 319 320 321 322 323 324
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
	const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL };
325
	struct disasm_line *dl;
326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
	s64 idx, offset;
	char *s = NULL;
	int i = 0;

	while (jumps[i]) {
		s = strstr(browser->selection->line, jumps[i++]);
		if (s)
			break;
	}

	if (s == NULL)
		return false;

	s = strchr(s, '+');
	if (s++ == NULL) {
		ui_helpline__puts("Invallid jump instruction.");
		return true;
	}

	offset = strtoll(s, NULL, 16);
346 347
	dl = annotate_browser__find_offset(browser, offset, &idx);
	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 583
			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.");
584
			continue;
585 586
		case K_LEFT:
		case K_ESC:
587 588
		case 'q':
		case CTRL('c'):
589
			goto out;
590 591
		default:
			continue;
592
		}
593 594

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

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

609
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
610
			 void(*timer)(void *arg), void *arg,
611
			 int delay_secs)
612
{
613
	struct disasm_line *pos, *n;
614
	struct annotation *notes;
615 616 617 618
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
619 620 621 622 623
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
624
			.filter  = disasm_line__filter,
625
			.priv	 = &ms,
626
			.use_navkeypressed = true,
627
		},
628 629 630
	};
	int ret;

631
	if (sym == NULL)
632 633
		return -1;

634
	if (map->dso->annotate_warned)
635 636
		return -1;

637
	if (symbol__annotate(sym, map, sizeof(struct disasm_line_rb_node)) < 0) {
638
		ui__error("%s", ui_helpline__last_msg);
639 640 641 642 643
		return -1;
	}

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

644
	notes = symbol__annotation(sym);
645
	browser.start = map__rip_2objdump(map, sym->start);
646

647
	list_for_each_entry(pos, &notes->src->source, node) {
648
		struct disasm_line_rb_node *rbpos;
649
		size_t line_len = strlen(pos->line);
650

651 652
		if (browser.b.width < line_len)
			browser.b.width = line_len;
653
		rbpos = disasm_line__rb(pos);
654 655 656 657 658
		rbpos->idx = browser.nr_entries++;
		if (pos->offset != -1)
			rbpos->idx_asm = browser.nr_asm_entries++;
		else
			rbpos->idx_asm = -1;
659 660
	}

661
	browser.b.nr_entries = browser.nr_entries;
662
	browser.b.entries = &notes->src->source,
663
	browser.b.width += 18; /* Percentage */
664
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
665
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
666
		list_del(&pos->node);
667
		disasm_line__free(pos);
668 669 670
	}
	return ret;
}