Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1fa2e84d authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge tag 'perf-annotate-for-mingo' of...

Merge tag 'perf-annotate-for-mingo' of git://git.kernel.org/pub/scm/linux/kernel/git/acme/linux

 into perf/core

Annotation improvements:

Now the default annotate browser uses a much more compact format, implementing
suggestions made made by several people, notably Linus.

Here is part of the new __list_del_entry() annotation:

__list_del_entry
    8.47 │      push   %rbp
    8.47 │      mov    (%rdi),%rdx
   20.34 │      mov    $0xdead000000100100,%rcx
    3.39 │      mov    0x8(%rdi),%rax
    0.00 │      mov    %rsp,%rbp
    1.69 │      cmp    %rcx,%rdx
    0.00 │      je     43
    1.69 │      mov    $0xdead000000200200,%rcx
    3.39 │      cmp    %rcx,%rax
    0.00 │      je     a3
    5.08 │      mov    (%rax),%r8
   18.64 │      cmp    %r8,%rdi
    0.00 │      jne    84
    1.69 │      mov    0x8(%rdx),%r8
   25.42 │      cmp    %r8,%rdi
    0.00 │      jne    65
    1.69 │      mov    %rax,0x8(%rdx)
    0.00 │      mov    %rdx,(%rax)
    0.00 │      leaveq
    0.00 │      retq
    0.00 │ 43:  mov    %rdx,%r8
    0.00 │      mov    %rdi,%rcx
    0.00 │      mov    $0xffffffff817cd6a8,%rdx
    0.00 │      mov    $0x31,%esi
    0.00 │      mov    $0xffffffff817cd6e0,%rdi
    0.00 │      xor    %eax,%eax
    0.00 │      callq  ffffffff8104eab0 <warn_slowpath_fmt>
    0.00 │      leaveq
    0.00 │      retq
    0.00 │ 65:  mov    %rdi,%rcx
    0.00 │      mov    $0xffffffff817cd780,%rdx
    0.00 │      mov    $0x3a,%esi
    0.00 │      mov    $0xffffffff817cd6e0,%rdi
    0.00 │      xor    %eax,%eax
    0.00 │      callq  ffffffff8104eab0 <warn_slowpath_fmt>
    0.00 │      leaveq
    0.00 │      retq

The infrastructure is there to provide formatters for any instruction,
like the one I'll do for call functions to elide the address.

Further fixes on top of the first iteration:

- Sometimes a jump points to an offset with no instructions, make the
  mark jump targets function handle that, for now just ignoring such
  jump targets, more investigation is needed to figure out how to cope
  with that.

- Handle jump targets that are outside the function, for now just don't
  try to draw the connector arrow, right thing seems to be to mark this
  jump with a -> (right arrow) and handle it like a callq.

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 392d65a9 38b31bd0
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -593,6 +593,52 @@ unsigned int ui_browser__argv_refresh(struct ui_browser *browser)
	return row;
}

void ui_browser__write_graph(struct ui_browser *browser __used, int graph)
{
	SLsmg_set_char_set(1);
	SLsmg_write_char(graph);
	SLsmg_set_char_set(0);
}

void __ui_browser__line_arrow_up(struct ui_browser *browser, unsigned int column,
				 u64 start, u64 end, int start_width)
{
	unsigned int row, end_row;

	SLsmg_set_char_set(1);

	if (start < browser->top_idx + browser->height) {
		row = start - browser->top_idx;
		ui_browser__gotorc(browser, row, column);
		SLsmg_write_char(SLSMG_LLCORN_CHAR);
		ui_browser__gotorc(browser, row, column + 1);
		SLsmg_draw_hline(start_width);

		if (row-- == 0)
			goto out;
	} else
		row = browser->height - 1;

	if (end > browser->top_idx)
		end_row = end - browser->top_idx;
	else
		end_row = 0;

	ui_browser__gotorc(browser, end_row, column);
	SLsmg_draw_vline(row - end_row + 1);

	ui_browser__gotorc(browser, end_row, column);
	if (end >= browser->top_idx) {
		SLsmg_write_char(SLSMG_ULCORN_CHAR);
		ui_browser__gotorc(browser, end_row, column + 1);
		SLsmg_write_char(SLSMG_HLINE_CHAR);
		ui_browser__gotorc(browser, end_row, column + 2);
		SLsmg_write_char(SLSMG_RARROW_CHAR);
	}
out:
	SLsmg_set_char_set(0);
}

void ui_browser__init(void)
{
	int i = 0;
+3 −0
Original line number Diff line number Diff line
@@ -37,6 +37,9 @@ void ui_browser__refresh_dimensions(struct ui_browser *self);
void ui_browser__reset_index(struct ui_browser *self);

void ui_browser__gotorc(struct ui_browser *self, int y, int x);
void ui_browser__write_graph(struct ui_browser *browser, int graph);
void __ui_browser__line_arrow_up(struct ui_browser *browser, unsigned int column,
				 u64 start, u64 end, int start_width);
void __ui_browser__show_title(struct ui_browser *browser, const char *title);
void ui_browser__show_title(struct ui_browser *browser, const char *title);
int ui_browser__show(struct ui_browser *self, const char *title,
+273 −144
Original line number Diff line number Diff line
@@ -11,40 +11,42 @@
#include <pthread.h>
#include <newt.h>

struct browser_disasm_line {
	struct rb_node	rb_node;
	double		percent;
	u32		idx;
	int		idx_asm;
	bool		jump_target;
};

struct annotate_browser {
	struct ui_browser b;
	struct rb_root	  entries;
	struct rb_node	  *curr_hot;
	struct objdump_line *selection;
	struct disasm_line	  *selection;
	struct disasm_line  **offsets;
	u64		    start;
	int		    nr_asm_entries;
	int		    nr_entries;
	bool		    hide_src_code;
	bool		    use_offset;
	bool		    searching_backwards;
	u8		    offset_width;
	char		    search_bf[128];
};

struct objdump_line_rb_node {
	struct rb_node	rb_node;
	double		percent;
	u32		idx;
	int		idx_asm;
};

static inline
struct objdump_line_rb_node *objdump_line__rb(struct objdump_line *self)
static inline struct browser_disasm_line *disasm_line__browser(struct disasm_line *dl)
{
	return (struct objdump_line_rb_node *)(self + 1);
	return (struct browser_disasm_line *)(dl + 1);
}

static bool objdump_line__filter(struct ui_browser *browser, void *entry)
static bool disasm_line__filter(struct ui_browser *browser, void *entry)
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);

	if (ab->hide_src_code) {
		struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
		return ol->offset == -1;
		struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
		return dl->offset == -1;
	}

	return false;
@@ -53,72 +55,160 @@ static bool objdump_line__filter(struct ui_browser *browser, void *entry)
static void annotate_browser__write(struct ui_browser *self, void *entry, int row)
{
	struct annotate_browser *ab = container_of(self, struct annotate_browser, b);
	struct objdump_line *ol = list_entry(entry, struct objdump_line, node);
	struct disasm_line *dl = list_entry(entry, struct disasm_line, node);
	struct browser_disasm_line *bdl = disasm_line__browser(dl);
	bool current_entry = ui_browser__is_current_entry(self, row);
	bool change_color = (!ab->hide_src_code &&
			     (!current_entry || (self->use_navkeypressed &&
					         !self->navkeypressed)));
	int width = self->width;

	if (ol->offset != -1) {
		struct objdump_line_rb_node *olrb = objdump_line__rb(ol);
		ui_browser__set_percent_color(self, olrb->percent, current_entry);
		slsmg_printf(" %7.2f ", olrb->percent);
	if (dl->offset != -1) {
		ui_browser__set_percent_color(self, bdl->percent, current_entry);
		slsmg_printf(" %7.2f ", bdl->percent);
	} else {
		ui_browser__set_percent_color(self, 0, current_entry);
		slsmg_write_nstring(" ", 9);
	}

	SLsmg_write_char(':');
	slsmg_write_nstring(" ", 8);
	ui_browser__write_graph(self, SLSMG_VLINE_CHAR);
	SLsmg_write_char(' ');

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

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

	if (!*ol->line)
		slsmg_write_nstring(" ", width - 18);
	else if (ol->offset == -1)
		slsmg_write_nstring(ol->line, width - 18);
	if (!*dl->line)
		slsmg_write_nstring(" ", width - 10);
	else if (dl->offset == -1)
		slsmg_write_nstring(dl->line, width - 10);
	else {
		char bf[64];
		u64 addr = ol->offset;
		char bf[256];
		u64 addr = dl->offset;
		int printed, color = -1;

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

		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, " ");
			}
		}

		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);
		slsmg_write_nstring(ol->line, width - 18 - printed);
		if (dl->ins && dl->ins->ops->scnprintf) {
			if (ins__is_jump(dl->ins)) {
				bool fwd = dl->ops.target.offset > (u64)dl->offset;

				ui_browser__write_graph(self, fwd ? SLSMG_DARROW_CHAR :
								    SLSMG_UARROW_CHAR);
				SLsmg_write_char(' ');
			} else {
				slsmg_write_nstring(" ", 2);
			}

			dl->ins->ops->scnprintf(dl->ins, bf, sizeof(bf), &dl->ops,
						!ab->use_offset);
		} else {
			if (strcmp(dl->name, "retq")) {
				slsmg_write_nstring(" ", 2);
			} else {
				ui_browser__write_graph(self, SLSMG_LARROW_CHAR);
				SLsmg_write_char(' ');
			}

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

		slsmg_write_nstring(bf, width - 12 - printed);
	}

	if (current_entry)
		ab->selection = ol;
		ab->selection = dl;
}

static void annotate_browser__draw_current_loop(struct ui_browser *browser)
{
	struct annotate_browser *ab = container_of(browser, struct annotate_browser, b);
	struct map_symbol *ms = browser->priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct disasm_line *cursor = ab->selection, *pos = cursor, *target;
	struct browser_disasm_line *bcursor = disasm_line__browser(cursor),
				   *btarget, *bpos;
	unsigned int from, to, start_width = 2;

	list_for_each_entry_from(pos, &notes->src->source, node) {
		if (!pos->ins || !ins__is_jump(pos->ins) ||
		    !disasm_line__has_offset(pos))
			continue;

		target = ab->offsets[pos->ops.target.offset];
		if (!target)
			continue;

		btarget = disasm_line__browser(target);
		if (btarget->idx <= bcursor->idx)
			goto found;
	}

	return;

found:
	bpos = disasm_line__browser(pos);
	if (ab->hide_src_code) {
		from = bpos->idx_asm;
		to = btarget->idx_asm;
	} else {
		from = (u64)bpos->idx;
		to = (u64)btarget->idx;
	}

	ui_browser__set_color(browser, HE_COLORSET_CODE);

	if (!bpos->jump_target)
		start_width += ab->offset_width + 1;

	__ui_browser__line_arrow_up(browser, 10, from, to, start_width);
}

static double objdump_line__calc_percent(struct objdump_line *self,
					 struct symbol *sym, int evidx)
static unsigned int annotate_browser__refresh(struct ui_browser *browser)
{
	int ret = ui_browser__list_head_refresh(browser);

	annotate_browser__draw_current_loop(browser);

	return ret;
}

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

	if (self->offset != -1) {
	if (dl->offset != -1) {
		int len = sym->end - sym->start;
		unsigned int hits = 0;
		struct annotation *notes = symbol__annotation(sym);
		struct source_line *src_line = notes->src->lines;
		struct sym_hist *h = annotation__histogram(notes, evidx);
		s64 offset = self->offset;
		struct objdump_line *next;
		s64 offset = dl->offset;
		struct disasm_line *next;

		next = objdump__get_next_ip_line(&notes->src->source, self);
		next = disasm__get_next_ip_line(&notes->src->source, dl);
		while (offset < (s64)len &&
		       (next == NULL || offset < next->offset)) {
			if (src_line) {
@@ -139,27 +229,26 @@ static double objdump_line__calc_percent(struct objdump_line *self,
	return percent;
}

static void objdump__insert_line(struct rb_root *self,
				 struct objdump_line_rb_node *line)
static void disasm_rb_tree__insert(struct rb_root *root, struct browser_disasm_line *bdl)
{
	struct rb_node **p = &self->rb_node;
	struct rb_node **p = &root->rb_node;
	struct rb_node *parent = NULL;
	struct objdump_line_rb_node *l;
	struct browser_disasm_line *l;

	while (*p != NULL) {
		parent = *p;
		l = rb_entry(parent, struct objdump_line_rb_node, rb_node);
		if (line->percent < l->percent)
		l = rb_entry(parent, struct browser_disasm_line, rb_node);
		if (bdl->percent < l->percent)
			p = &(*p)->rb_left;
		else
			p = &(*p)->rb_right;
	}
	rb_link_node(&line->rb_node, parent, p);
	rb_insert_color(&line->rb_node, self);
	rb_link_node(&bdl->rb_node, parent, p);
	rb_insert_color(&bdl->rb_node, root);
}

static void annotate_browser__set_top(struct annotate_browser *self,
				      struct objdump_line *pos, u32 idx)
				      struct disasm_line *pos, u32 idx)
{
	unsigned back;

@@ -168,9 +257,9 @@ static void annotate_browser__set_top(struct annotate_browser *self,
	self->b.top_idx = self->b.index = idx;

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

		if (objdump_line__filter(&self->b, &pos->node))
		if (disasm_line__filter(&self->b, &pos->node))
			continue;

		--self->b.top_idx;
@@ -184,12 +273,12 @@ static void annotate_browser__set_top(struct annotate_browser *self,
static void annotate_browser__set_rb_top(struct annotate_browser *browser,
					 struct rb_node *nd)
{
	struct objdump_line_rb_node *rbpos;
	struct objdump_line *pos;
	struct browser_disasm_line *bpos;
	struct disasm_line *pos;

	rbpos = rb_entry(nd, struct objdump_line_rb_node, rb_node);
	pos = ((struct objdump_line *)rbpos) - 1;
	annotate_browser__set_top(browser, pos, rbpos->idx);
	bpos = rb_entry(nd, struct browser_disasm_line, rb_node);
	pos = ((struct disasm_line *)bpos) - 1;
	annotate_browser__set_top(browser, pos, bpos->idx);
	browser->curr_hot = nd;
}

@@ -199,20 +288,20 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct objdump_line *pos;
	struct disasm_line *pos;

	browser->entries = RB_ROOT;

	pthread_mutex_lock(&notes->lock);

	list_for_each_entry(pos, &notes->src->source, node) {
		struct objdump_line_rb_node *rbpos = objdump_line__rb(pos);
		rbpos->percent = objdump_line__calc_percent(pos, sym, evidx);
		if (rbpos->percent < 0.01) {
			RB_CLEAR_NODE(&rbpos->rb_node);
		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);
			continue;
		}
		objdump__insert_line(&browser->entries, rbpos);
		disasm_rb_tree__insert(&browser->entries, bpos);
	}
	pthread_mutex_unlock(&notes->lock);

@@ -221,38 +310,38 @@ static void annotate_browser__calc_percent(struct annotate_browser *browser,

static bool annotate_browser__toggle_source(struct annotate_browser *browser)
{
	struct objdump_line *ol;
	struct objdump_line_rb_node *olrb;
	struct disasm_line *dl;
	struct browser_disasm_line *bdl;
	off_t offset = browser->b.index - browser->b.top_idx;

	browser->b.seek(&browser->b, offset, SEEK_CUR);
	ol = list_entry(browser->b.top, struct objdump_line, node);
	olrb = objdump_line__rb(ol);
	dl = list_entry(browser->b.top, struct disasm_line, node);
	bdl = disasm_line__browser(dl);

	if (browser->hide_src_code) {
		if (olrb->idx_asm < offset)
			offset = olrb->idx;
		if (bdl->idx_asm < offset)
			offset = bdl->idx;

		browser->b.nr_entries = browser->nr_entries;
		browser->hide_src_code = false;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
		browser->b.top_idx = olrb->idx - offset;
		browser->b.index = olrb->idx;
		browser->b.top_idx = bdl->idx - offset;
		browser->b.index = bdl->idx;
	} else {
		if (olrb->idx_asm < 0) {
		if (bdl->idx_asm < 0) {
			ui_helpline__puts("Only available for assembly lines.");
			browser->b.seek(&browser->b, -offset, SEEK_CUR);
			return false;
		}

		if (olrb->idx_asm < offset)
			offset = olrb->idx_asm;
		if (bdl->idx_asm < offset)
			offset = bdl->idx_asm;

		browser->b.nr_entries = browser->nr_asm_entries;
		browser->hide_src_code = true;
		browser->b.seek(&browser->b, -offset, SEEK_CUR);
		browser->b.top_idx = olrb->idx_asm - offset;
		browser->b.index = olrb->idx_asm;
		browser->b.top_idx = bdl->idx_asm - offset;
		browser->b.index = bdl->idx_asm;
	}

	return true;
@@ -263,23 +352,16 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
				    void *arg, int delay_secs)
{
	struct map_symbol *ms = browser->b.priv;
	struct disasm_line *dl = browser->selection;
	struct symbol *sym = ms->sym;
	struct annotation *notes;
	struct symbol *target;
	char *s = strstr(browser->selection->line, "callq ");
	u64 ip;

	if (s == NULL)
	if (!ins__is_call(dl->ins))
		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);
	ip = ms->map->map_ip(ms->map, dl->ops.target.addr);
	target = map__find_symbol(ms->map, ip, NULL);
	if (target == NULL) {
		ui_helpline__puts("The called function was not found.");
@@ -302,20 +384,20 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
	return true;
}

static struct objdump_line *
	annotate_browser__find_offset(struct annotate_browser *browser,
static
struct disasm_line *annotate_browser__find_offset(struct annotate_browser *browser,
					  s64 offset, s64 *idx)
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct objdump_line *pos;
	struct disasm_line *pos;

	*idx = 0;
	list_for_each_entry(pos, &notes->src->source, node) {
		if (pos->offset == offset)
			return pos;
		if (!objdump_line__filter(&browser->b, &pos->node))
		if (!disasm_line__filter(&browser->b, &pos->node))
			++*idx;
	}

@@ -324,51 +406,35 @@ static struct objdump_line *

static bool annotate_browser__jump(struct annotate_browser *browser)
{
	const char *jumps[] = { "je ", "jne ", "ja ", "jmpq ", "js ", "jmp ", NULL };
	struct objdump_line *line;
	s64 idx, offset;
	char *s = NULL;
	int i = 0;

	while (jumps[i]) {
		s = strstr(browser->selection->line, jumps[i++]);
		if (s)
			break;
	}
	struct disasm_line *dl = browser->selection;
	s64 idx;

	if (s == NULL)
	if (!ins__is_jump(dl->ins))
		return false;

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

	offset = strtoll(s, NULL, 16);
	line = annotate_browser__find_offset(browser, offset, &idx);
	if (line == NULL) {
	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
	if (dl == NULL) {
		ui_helpline__puts("Invallid jump offset");
		return true;
	}

	annotate_browser__set_top(browser, line, idx);
	annotate_browser__set_top(browser, dl, idx);
	
	return true;
}

static struct objdump_line *
	annotate_browser__find_string(struct annotate_browser *browser,
static
struct disasm_line *annotate_browser__find_string(struct annotate_browser *browser,
					  char *s, s64 *idx)
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct objdump_line *pos = browser->selection;
	struct disasm_line *pos = browser->selection;

	*idx = browser->b.index;
	list_for_each_entry_continue(pos, &notes->src->source, node) {
		if (objdump_line__filter(&browser->b, &pos->node))
		if (disasm_line__filter(&browser->b, &pos->node))
			continue;

		++*idx;
@@ -382,32 +448,32 @@ static struct objdump_line *

static bool __annotate_browser__search(struct annotate_browser *browser)
{
	struct objdump_line *line;
	struct disasm_line *dl;
	s64 idx;

	line = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (line == NULL) {
	dl = annotate_browser__find_string(browser, browser->search_bf, &idx);
	if (dl == NULL) {
		ui_helpline__puts("String not found!");
		return false;
	}

	annotate_browser__set_top(browser, line, idx);
	annotate_browser__set_top(browser, dl, idx);
	browser->searching_backwards = false;
	return true;
}

static struct objdump_line *
	annotate_browser__find_string_reverse(struct annotate_browser *browser,
static
struct disasm_line *annotate_browser__find_string_reverse(struct annotate_browser *browser,
						  char *s, s64 *idx)
{
	struct map_symbol *ms = browser->b.priv;
	struct symbol *sym = ms->sym;
	struct annotation *notes = symbol__annotation(sym);
	struct objdump_line *pos = browser->selection;
	struct disasm_line *pos = browser->selection;

	*idx = browser->b.index;
	list_for_each_entry_continue_reverse(pos, &notes->src->source, node) {
		if (objdump_line__filter(&browser->b, &pos->node))
		if (disasm_line__filter(&browser->b, &pos->node))
			continue;

		--*idx;
@@ -421,16 +487,16 @@ static struct objdump_line *

static bool __annotate_browser__search_reverse(struct annotate_browser *browser)
{
	struct objdump_line *line;
	struct disasm_line *dl;
	s64 idx;

	line = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (line == NULL) {
	dl = annotate_browser__find_string_reverse(browser, browser->search_bf, &idx);
	if (dl == NULL) {
		ui_helpline__puts("String not found!");
		return false;
	}

	annotate_browser__set_top(browser, line, idx);
	annotate_browser__set_top(browser, dl, idx);
	browser->searching_backwards = true;
	return true;
}
@@ -581,9 +647,15 @@ show_help:
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
			else if (self->selection->offset == -1)
				ui_helpline__puts("Actions are only available for assembly lines.");
			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.");
			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.");
			}
			continue;
		case K_LEFT:
		case K_ESC:
@@ -609,27 +681,63 @@ int hist_entry__tui_annotate(struct hist_entry *he, int evidx,
				    timer, arg, delay_secs);
}

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) ||
		    !disasm_line__has_offset(dl))
			continue;

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

		dlt = browser->offsets[dl->ops.target.offset];
		/*
 		 * FIXME: Oops, no jump target? Buggy disassembler? Or do we
 		 * have to adjust to the previous offset?
 		 */
		if (dlt == NULL)
			continue;

		bdlt = disasm_line__browser(dlt);
		bdlt->jump_target = true;
	}
		
}

int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
			 void(*timer)(void *arg), void *arg,
			 int delay_secs)
{
	struct objdump_line *pos, *n;
	struct disasm_line *pos, *n;
	struct annotation *notes;
	const size_t size = symbol__size(sym);
	struct map_symbol ms = {
		.map = map,
		.sym = sym,
	};
	struct annotate_browser browser = {
		.b = {
			.refresh = ui_browser__list_head_refresh,
			.refresh = annotate_browser__refresh,
			.seek	 = ui_browser__list_head_seek,
			.write	 = annotate_browser__write,
			.filter  = objdump_line__filter,
			.filter  = disasm_line__filter,
			.priv	 = &ms,
			.use_navkeypressed = true,
		},
		.use_offset = true,
	};
	int ret;
	int ret = -1;

	if (sym == NULL)
		return -1;
@@ -637,37 +745,58 @@ int symbol__tui_annotate(struct symbol *sym, struct map *map, int evidx,
	if (map->dso->annotate_warned)
		return -1;

	if (symbol__annotate(sym, map, sizeof(struct objdump_line_rb_node)) < 0) {
		ui__error("%s", ui_helpline__last_msg);
	browser.offsets = zalloc(size * sizeof(struct disasm_line *));
	if (browser.offsets == NULL) {
		ui__error("Not enough memory!");
		return -1;
	}

	if (symbol__annotate(sym, map, sizeof(struct browser_disasm_line)) < 0) {
		ui__error("%s", ui_helpline__last_msg);
		goto out_free_offsets;
	}

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

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

	list_for_each_entry(pos, &notes->src->source, node) {
		struct objdump_line_rb_node *rbpos;
		struct browser_disasm_line *bpos;
		size_t line_len = strlen(pos->line);

		if (browser.b.width < line_len)
			browser.b.width = line_len;
		rbpos = objdump_line__rb(pos);
		rbpos->idx = browser.nr_entries++;
		if (pos->offset != -1)
			rbpos->idx_asm = browser.nr_asm_entries++;
		else
			rbpos->idx_asm = -1;
		bpos = disasm_line__browser(pos);
		bpos->idx = browser.nr_entries++;
		if (pos->offset != -1) {
			bpos->idx_asm = browser.nr_asm_entries++;
			/*
			 * 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;
		} else
			bpos->idx_asm = -1;
	}

	annotate_browser__mark_jump_targets(&browser, size);

	browser.offset_width = hex_width(size);
	browser.b.nr_entries = browser.nr_entries;
	browser.b.entries = &notes->src->source,
	browser.b.width += 18; /* Percentage */
	ret = annotate_browser__run(&browser, evidx, timer, arg, delay_secs);
	list_for_each_entry_safe(pos, n, &notes->src->source, node) {
		list_del(&pos->node);
		objdump_line__free(pos);
		disasm_line__free(pos);
	}

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