annotate.c 16.1 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
	struct disasm_line *dl = browser->selection;
319
	s64 idx;
320

321
	if (!dl->ins || !ins__is_jump(dl->ins))
322 323
		return false;

324
	dl = annotate_browser__find_offset(browser, dl->target, &idx);
325
	if (dl == NULL) {
326 327 328 329
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

330
	annotate_browser__set_top(browser, dl, idx);
331 332 333 334
	
	return true;
}

335 336 337
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
338 339 340 341
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
342
	struct disasm_line *pos = browser->selection;
343 344 345

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
346
		if (disasm_line__filter(&browser->b, &pos->node))
347 348 349 350 351 352 353 354 355 356 357 358 359
			continue;

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
360
	struct disasm_line *dl;
361 362
	s64 idx;

363 364
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
365 366 367 368
		ui_helpline__puts("String not found!");
		return false;
	}

369
	annotate_browser__set_top(browser, dl, idx);
370 371 372 373
	browser->searching_backwards = false;
	return true;
}

374 375 376
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
377 378 379 380
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
381
	struct disasm_line *pos = browser->selection;
382 383 384

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
385
		if (disasm_line__filter(&browser->b, &pos->node))
386 387 388 389 390 391 392 393 394 395 396 397 398
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
399
	struct disasm_line *dl;
400 401
	s64 idx;

402 403
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
404 405 406 407
		ui_helpline__puts("String not found!");
		return false;
	}

408
	annotate_browser__set_top(browser, dl, idx);
409 410 411 412 413 414 415 416 417 418 419 420 421 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
	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);
}

461
static int annotate_browser__run(struct annotate_browser *self, int evidx,
462
				 void(*timer)(void *arg),
463
				 void *arg, int delay_secs)
464 465
{
	struct rb_node *nd = NULL;
466 467
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
468 469
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Go to hottest line, ->/ENTER: Line action, "
470
			   "O: Toggle offset view, "
471
			   "S: Toggle source code view";
472
	int key;
473

474
	if (ui_browser__show(&self->b, sym->name, help) < 0)
475
		return -1;
476 477 478

	annotate_browser__calc_percent(self, evidx);

479
	if (self->curr_hot) {
480
		annotate_browser__set_rb_top(self, self->curr_hot);
481 482
		self->b.navkeypressed = false;
	}
483 484

	nd = self->curr_hot;
485

486
	while (1) {
487
		key = ui_browser__run(&self->b, delay_secs);
488

489
		if (delay_secs != 0) {
490 491 492 493 494 495 496 497 498 499
			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;
		}

500
		switch (key) {
501
		case K_TIMER:
502 503 504 505
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
506 507
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
508
		case K_TAB:
509 510 511 512 513 514
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
					nd = rb_last(&self->entries);
			} else
				nd = self->curr_hot;
515
			break;
516
		case K_UNTAB:
517 518 519 520 521 522 523 524
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
					nd = rb_first(&self->entries);
			else
				nd = self->curr_hot;
			break;
		case 'H':
525
		case 'h':
526
			nd = self->curr_hot;
527
			break;
528
		case 'S':
529
		case 's':
530 531 532
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
533 534 535 536
		case 'O':
		case 'o':
			self->use_offset = !self->use_offset;
			continue;
537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552
		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;
553 554
		case K_ENTER:
		case K_RIGHT:
555
			if (self->selection == NULL)
556
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
557
			else if (self->selection->offset == -1)
558
				ui_helpline__puts("Actions are only available for assembly lines.");
559 560 561
			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.");
562
			continue;
563 564
		case K_LEFT:
		case K_ESC:
565 566
		case 'q':
		case CTRL('c'):
567
			goto out;
568 569
		default:
			continue;
570
		}
571 572

		if (nd != NULL)
573
			annotate_browser__set_rb_top(self, nd);
574 575
	}
out:
576
	ui_browser__hide(&self->b);
577
	return key;
578 579
}

580
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
581
			     void(*timer)(void *arg), void *arg, int delay_secs)
582
{
583
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
584
				    timer, arg, delay_secs);
585 586
}

587
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
588
			 void(*timer)(void *arg), void *arg,
589
			 int delay_secs)
590
{
591
	struct disasm_line *pos, *n;
592
	struct annotation *notes;
593 594 595 596
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
597 598 599 600 601
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
602
			.filter  = disasm_line__filter,
603
			.priv	 = &ms,
604
			.use_navkeypressed = true,
605
		},
606 607 608
	};
	int ret;

609
	if (sym == NULL)
610 611
		return -1;

612
	if (map->dso->annotate_warned)
613 614
		return -1;

615
	if (symbol__annotate(sym, map, sizeof(struct disasm_line_rb_node)) < 0) {
616
		ui__error("%s", ui_helpline__last_msg);
617 618 619 620 621
		return -1;
	}

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

622
	notes = symbol__annotation(sym);
623
	browser.start = map__rip_2objdump(map, sym->start);
624

625
	list_for_each_entry(pos, &notes->src->source, node) {
626
		struct disasm_line_rb_node *rbpos;
627
		size_t line_len = strlen(pos->line);
628

629 630
		if (browser.b.width < line_len)
			browser.b.width = line_len;
631
		rbpos = disasm_line__rb(pos);
632 633 634 635 636
		rbpos->idx = browser.nr_entries++;
		if (pos->offset != -1)
			rbpos->idx_asm = browser.nr_asm_entries++;
		else
			rbpos->idx_asm = -1;
637 638
	}

639
	browser.b.nr_entries = browser.nr_entries;
640
	browser.b.entries = &notes->src->source,
641
	browser.b.width += 18; /* Percentage */
642
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
643
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
644
		list_del(&pos->node);
645
		disasm_line__free(pos);
646 647 648
	}
	return ret;
}