annotate.c 18.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 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
	ui_browser__write_graph(self, SLSMG_VLINE_CHAR);
75
	SLsmg_write_char(' ');
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 - 10);
86
	else if (dl->offset == -1)
87
		slsmg_write_nstring(dl->line, width - 10);
88
	else {
89
		char bf[256];
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
		if (dl->ins && dl->ins->ops->scnprintf) {
114 115 116
			if (ins__is_jump(dl->ins)) {
				bool fwd = dl->ops.target > (u64)dl->offset;

117 118
				ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR :
								    SLSMG_UARROW_CHAR);
119 120 121 122 123
				SLsmg_write_char(' ');
			} else {
				slsmg_write_nstring(" ", 2);
			}

124 125
			dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf), &dl->ops,
						!ab->use_offset);
126 127 128 129
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
130
				ui_browser__write_graph(self, SLSMG_LARROW_CHAR);
131 132 133 134 135
				SLsmg_write_char(' ');
			}

			scnprintf(bf, sizeof(bf), "%-6.6s %s", dl->name, dl->ops.raw);
		}
136

137
		slsmg_write_nstring(bf, width - 12 - printed);
138
	}
139

140
	if (current_entry)
141
		ab->selection = dl;
142 143
}

144
static double disasm_line__calc_percent(struct disasm_line *dl, struct symbol *sym, int evidx)
145 146 147
{
	double percent = 0.0;

148
	if (dl->offset != -1) {
149
		int len = sym->end - sym->start;
150
		unsigned int hits = 0;
151
		struct annotation *notes = symbol__annotation(sym);
152
		struct source_line *src_line = notes->src->lines;
153
		struct sym_hist *h = annotation__histogram(notes, evidx);
154 155
		s64 offset = dl->offset;
		struct disasm_line *next;
156

157
		next = disasm__get_next_ip_line(&notes->src->source, dl);
158 159
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
160 161
			if (src_line) {
				percent += src_line[offset].percent;
162
			} else
163
				hits += h->addr[offset];
164 165 166

			++offset;
		}
167 168 169 170 171
		/*
 		 * If the percentage wasn't already calculated in
 		 * symbol__get_source_line, do it now:
 		 */
		if (src_line == NULL && h->sum)
172 173 174
			percent = 100.0 * hits / h->sum;
	}

175 176 177
	return percent;
}

178
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
179
{
180
	struct rb_node **p = &root->rb_node;
181
	struct rb_node *parent = NULL;
182
	struct browser_disasm_line *l;
183 184 185

	while (*p != NULL) {
		parent = *p;
186 187
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
188 189 190 191
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
192 193
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
194 195
}

196
static void annotate_browser__set_top(struct annotate_browser *self,
197
				      struct disasm_line *pos, u32 idx)
198 199 200 201 202
{
	unsigned back;

	ui_browser__refresh_dimensions(&self->b);
	back = self->b.height / 2;
203
	self->b.top_idx = self->b.index = idx;
204 205

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

208
		if (disasm_line__filter(&self->b, &pos->node))
209 210
			continue;

211 212 213 214 215
		--self->b.top_idx;
		--back;
	}

	self->b.top = pos;
216
	self->b.navkeypressed = true;
217 218 219 220 221
}

static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
222
	struct browser_disasm_line *bpos;
223
	struct disasm_line *pos;
224

225 226 227
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
	annotate_browser__set_top(browser, pos, bpos->idx);
228
	browser->curr_hot = nd;
229 230
}

231 232
static void annotate_browser__calc_percent(struct annotate_browser *browser,
					   int evidx)
233
{
234 235
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
236
	struct annotation *notes = symbol__annotation(sym);
237
	struct disasm_line *pos;
238 239 240 241 242 243

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
244 245 246 247
		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);
248 249
			continue;
		}
250
		disasm_rb_tree__insert(&browser->entries, bpos);
251 252 253 254 255 256
	}
	pthread_mutex_unlock(&notes->lock);

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

257 258
static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
259
	struct disasm_line *dl;
260
	struct browser_disasm_line *bdl;
261 262 263
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
264
	dl = list_entry(browser->b.top, struct disasm_line, node);
265
	bdl = disasm_line__browser(dl);
266 267

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

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
274 275
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
276
	} else {
277
		if (bdl->idx_asm < 0) {
278 279 280 281 282
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

283 284
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;
285 286 287 288

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
289 290
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
291 292 293 294 295
	}

	return true;
}

296 297 298 299 300
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;
301
	struct disasm_line *dl = browser->selection;
302 303 304 305 306
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	u64 ip;

307
	if (!ins__is_call(dl->ins))
308 309
		return false;

310
	ip = ms->map->map_ip(ms->map, dl->ops.target);
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
	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;
}

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

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
346
		if (!disasm_line__filter(&browser->b, &pos->node))
347 348 349 350 351 352 353 354
			++*idx;
	}

	return NULL;
}

static bool annotate_browser__jump(struct annotate_browser *browser)
{
355
	struct disasm_line *dl = browser->selection;
356
	s64 idx;
357

358
	if (!ins__is_jump(dl->ins))
359 360
		return false;

361
	dl = annotate_browser__find_offset(browser, dl->ops.target, &idx);
362
	if (dl == NULL) {
363 364 365 366
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

367
	annotate_browser__set_top(browser, dl, idx);
368 369 370 371
	
	return true;
}

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

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

		++*idx;

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

	return NULL;
}

static bool __annotate_browser__search(struct annotate_browser *browser)
{
397
	struct disasm_line *dl;
398 399
	s64 idx;

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

406
	annotate_browser__set_top(browser, dl, idx);
407 408 409 410
	browser->searching_backwards = false;
	return true;
}

411 412 413
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
414 415 416 417
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
418
	struct disasm_line *pos = browser->selection;
419 420 421

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
422
		if (disasm_line__filter(&browser->b, &pos->node))
423 424 425 426 427 428 429 430 431 432 433 434 435
			continue;

		--*idx;

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

	return NULL;
}

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
436
	struct disasm_line *dl;
437 438
	s64 idx;

439 440
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
441 442 443 444
		ui_helpline__puts("String not found!");
		return false;
	}

445
	annotate_browser__set_top(browser, dl, idx);
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 484 485 486 487 488 489 490 491 492 493 494 495 496 497
	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);
}

498
static int annotate_browser__run(struct annotate_browser *self, int evidx,
499
				 void(*timer)(void *arg),
500
				 void *arg, int delay_secs)
501 502
{
	struct rb_node *nd = NULL;
503 504
	struct map_symbol *ms = self->b.priv;
	struct symbol *sym = ms->sym;
505 506
	const char *help = "<-/ESC: Exit, TAB/shift+TAB: Cycle hot lines, "
			   "H: Go to hottest line, ->/ENTER: Line action, "
507
			   "O: Toggle offset view, "
508
			   "S: Toggle source code view";
509
	int key;
510

511
	if (ui_browser__show(&self->b, sym->name, help) < 0)
512
		return -1;
513 514 515

	annotate_browser__calc_percent(self, evidx);

516
	if (self->curr_hot) {
517
		annotate_browser__set_rb_top(self, self->curr_hot);
518 519
		self->b.navkeypressed = false;
	}
520 521

	nd = self->curr_hot;
522

523
	while (1) {
524
		key = ui_browser__run(&self->b, delay_secs);
525

526
		if (delay_secs != 0) {
527 528 529 530 531 532 533 534 535 536
			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;
		}

537
		switch (key) {
538
		case K_TIMER:
539 540 541 542
			if (timer != NULL)
				timer(arg);

			if (delay_secs != 0)
543 544
				symbol__annotate_decay_histogram(sym, evidx);
			continue;
545
		case K_TAB:
546 547 548 549 550 551
			if (nd != NULL) {
				nd = rb_prev(nd);
				if (nd == NULL)
					nd = rb_last(&self->entries);
			} else
				nd = self->curr_hot;
552
			break;
553
		case K_UNTAB:
554 555 556 557 558 559 560 561
			if (nd != NULL)
				nd = rb_next(nd);
				if (nd == NULL)
					nd = rb_first(&self->entries);
			else
				nd = self->curr_hot;
			break;
		case 'H':
562
		case 'h':
563
			nd = self->curr_hot;
564
			break;
565
		case 'S':
566
		case 's':
567 568 569
			if (annotate_browser__toggle_source(self))
				ui_helpline__puts(help);
			continue;
570 571 572 573
		case 'O':
		case 'o':
			self->use_offset = !self->use_offset;
			continue;
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589
		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;
590 591
		case K_ENTER:
		case K_RIGHT:
592
			if (self->selection == NULL)
593
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
594
			else if (self->selection->offset == -1)
595
				ui_helpline__puts("Actions are only available for assembly lines.");
596 597 598 599 600 601 602 603 604
			else if (!self->selection->ins) {
				if (strcmp(self->selection->name, "retq"))
					goto show_sup_ins;
				goto out;
			} else if (!(annotate_browser__jump(self) ||
				     annotate_browser__callq(self, evidx, timer, arg, delay_secs))) {
show_sup_ins:
				ui_helpline__puts("Actions are only available for 'callq', 'retq' & jump instructions.");
			}
605
			continue;
606 607
		case K_LEFT:
		case K_ESC:
608 609
		case 'q':
		case CTRL('c'):
610
			goto out;
611 612
		default:
			continue;
613
		}
614 615

		if (nd != NULL)
616
			annotate_browser__set_rb_top(self, nd);
617 618
	}
out:
619
	ui_browser__hide(&self->b);
620
	return key;
621 622
}

623
int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
624
			     void(*timer)(void *arg), void *arg, int delay_secs)
625
{
626
	return symbol__tui_annotate(he->ms.sym, he->ms.map, evidx,
627
				    timer, arg, delay_secs);
628 629
}

630 631 632 633 634 635 636 637 638 639 640 641
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;

642
		if (dl->ops.target >= size) {
643 644
			ui__error("jump to after symbol!\n"
				  "size: %zx, jump target: %" PRIx64,
645
				  size, dl->ops.target);
646 647 648
			continue;
		}

649
		dlt = browser->offsets[dl->ops.target];
650 651 652 653 654 655
		bdlt = disasm_line__browser(dlt);
		bdlt->jump_target = true;
	}
		
}

656
int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
657
			 void(*timer)(void *arg), void *arg,
658
			 int delay_secs)
659
{
660
	struct disasm_line *pos, *n;
661
	struct annotation *notes;
662
	const size_t size = symbol__size(sym);
663 664 665 666
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
667 668 669 670 671
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
672
			.filter  = disasm_line__filter,
673
			.priv	 = &ms,
674
			.use_navkeypressed = true,
675
		},
676
		.use_offset = true,
677
	};
678
	int ret = -1;
679

680
	if (sym == NULL)
681 682
		return -1;

683
	if (map->dso->annotate_warned)
684 685
		return -1;

686 687 688 689 690 691
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

692
	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
693
		ui__error("%s", ui_helpline__last_msg);
694
		goto out_free_offsets;
695 696 697 698
	}

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

699
	notes = symbol__annotation(sym);
700
	browser.start = map__rip_2objdump(map, sym->start);
701

702
	list_for_each_entry(pos, &notes->src->source, node) {
703
		struct browser_disasm_line *bpos;
704
		size_t line_len = strlen(pos->line);
705

706 707
		if (browser.b.width < line_len)
			browser.b.width = line_len;
708 709
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
710
		if (pos->offset != -1) {
711
			bpos->idx_asm = browser.nr_asm_entries++;
712 713 714 715 716 717 718 719 720
			/*
			 * FIXME: short term bandaid to cope with assembly
			 * routines that comes with labels in the same column
			 * as the address in objdump, sigh.
			 *
			 * E.g. copy_user_generic_unrolled
 			 */
			if (pos->offset < (s64)size)
				browser.offsets[pos->offset] = pos;
721
		} else
722
			bpos->idx_asm = -1;
723 724
	}

725 726
	annotate_browser__mark_jump_targets(&browser, size);

727
	browser.offset_width = hex_width(size);
728
	browser.b.nr_entries = browser.nr_entries;
729
	browser.b.entries = &notes->src->source,
730
	browser.b.width += 18; /* Percentage */
731
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
732
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
733
		list_del(&pos->node);
734
		disasm_line__free(pos);
735
	}
736 737 738

out_free_offsets:
	free(browser.offsets);
739 740
	return ret;
}