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

Commit 2471cece authored by Ingo Molnar's avatar Ingo Molnar
Browse files

Merge tag 'perf-core-for-mingo-20161125' of...

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

 into perf/core

Pull perf/core improvements and fixes from Arnaldo Carvalho de Melo:

New features:

- Improve ARM support in the annotation code, affecting 'perf annotate', 'perf
  report' and live annotation in 'perf top' (Kim Phillips)

- Initial support for PowerPC in the annotation code (Ravi Bangoria)

- Skip repetitive scheduler function on the top of the stack in
  'perf sched timehist' (Namhyung Kim)

Fixes:

- Fix maps resolution in libbpf (Eric Leblond)

- Get the kernel signature via /proc/version_signature, available on
  Ubuntu systems, to make sure BPF proggies works, as the one provided
  via 'uname -r' doesn't (Wang Nan)

- Fix segfault in 'perf record' when running with suid and kptr_restrict
  is 1 (Wang Nan)

Infrastructure changes:

- Support per-arch instruction tables, kept via a static or dynamic table
  (Arnaldo Carvalho de Melo)

Signed-off-by: default avatarArnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: default avatarIngo Molnar <mingo@kernel.org>
parents 47414424 4708bbda
Loading
Loading
Loading
Loading
+98 −44
Original line number Diff line number Diff line
@@ -185,6 +185,7 @@ struct bpf_program {
struct bpf_map {
	int fd;
	char *name;
	size_t offset;
	struct bpf_map_def def;
	void *priv;
	bpf_map_clear_priv_t clear_priv;
@@ -513,20 +514,83 @@ bpf_object__init_kversion(struct bpf_object *obj,
}

static int
bpf_object__init_maps(struct bpf_object *obj, void *data,
		      size_t size)
bpf_object__validate_maps(struct bpf_object *obj)
{
	size_t nr_maps;
	int i;

	nr_maps = size / sizeof(struct bpf_map_def);
	if (!data || !nr_maps) {
		pr_debug("%s doesn't need map definition\n",
			 obj->path);
	/*
	 * If there's only 1 map, the only error case should have been
	 * catched in bpf_object__init_maps().
	 */
	if (!obj->maps || !obj->nr_maps || (obj->nr_maps == 1))
		return 0;

	for (i = 1; i < obj->nr_maps; i++) {
		const struct bpf_map *a = &obj->maps[i - 1];
		const struct bpf_map *b = &obj->maps[i];

		if (b->offset - a->offset < sizeof(struct bpf_map_def)) {
			pr_warning("corrupted map section in %s: map \"%s\" too small\n",
				   obj->path, a->name);
			return -EINVAL;
		}
	}
	return 0;
}

	pr_debug("maps in %s: %zd bytes\n", obj->path, size);
static int compare_bpf_map(const void *_a, const void *_b)
{
	const struct bpf_map *a = _a;
	const struct bpf_map *b = _b;

	return a->offset - b->offset;
}

static int
bpf_object__init_maps(struct bpf_object *obj)
{
	int i, map_idx, nr_maps = 0;
	Elf_Scn *scn;
	Elf_Data *data;
	Elf_Data *symbols = obj->efile.symbols;

	if (obj->efile.maps_shndx < 0)
		return -EINVAL;
	if (!symbols)
		return -EINVAL;

	scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
	if (scn)
		data = elf_getdata(scn, NULL);
	if (!scn || !data) {
		pr_warning("failed to get Elf_Data from map section %d\n",
			   obj->efile.maps_shndx);
		return -EINVAL;
	}

	/*
	 * Count number of maps. Each map has a name.
	 * Array of maps is not supported: only the first element is
	 * considered.
	 *
	 * TODO: Detect array of map and report error.
	 */
	for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
		GElf_Sym sym;

		if (!gelf_getsym(symbols, i, &sym))
			continue;
		if (sym.st_shndx != obj->efile.maps_shndx)
			continue;
		nr_maps++;
	}

	/* Alloc obj->maps and fill nr_maps. */
	pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
		 nr_maps, data->d_size);

	if (!nr_maps)
		return 0;

	obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
	if (!obj->maps) {
@@ -535,35 +599,21 @@ bpf_object__init_maps(struct bpf_object *obj, void *data,
	}
	obj->nr_maps = nr_maps;

	for (i = 0; i < nr_maps; i++) {
		struct bpf_map_def *def = &obj->maps[i].def;

	/*
	 * fill all fd with -1 so won't close incorrect
	 * fd (fd=0 is stdin) when failure (zclose won't close
	 * negative fd)).
	 */
	for (i = 0; i < nr_maps; i++)
		obj->maps[i].fd = -1;

		/* Save map definition into obj->maps */
		*def = ((struct bpf_map_def *)data)[i];
	}
	return 0;
}

static int
bpf_object__init_maps_name(struct bpf_object *obj)
{
	int i;
	Elf_Data *symbols = obj->efile.symbols;

	if (!symbols || obj->efile.maps_shndx < 0)
		return -EINVAL;

	for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
	/*
	 * Fill obj->maps using data in "maps" section.
	 */
	for (i = 0, map_idx = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
		GElf_Sym sym;
		size_t map_idx;
		const char *map_name;
		struct bpf_map_def *def;

		if (!gelf_getsym(symbols, i, &sym))
			continue;
@@ -573,21 +623,27 @@ bpf_object__init_maps_name(struct bpf_object *obj)
		map_name = elf_strptr(obj->efile.elf,
				      obj->efile.strtabidx,
				      sym.st_name);
		map_idx = sym.st_value / sizeof(struct bpf_map_def);
		if (map_idx >= obj->nr_maps) {
			pr_warning("index of map \"%s\" is buggy: %zu > %zu\n",
				   map_name, map_idx, obj->nr_maps);
			continue;
		obj->maps[map_idx].offset = sym.st_value;
		if (sym.st_value + sizeof(struct bpf_map_def) > data->d_size) {
			pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
				   obj->path, map_name);
			return -EINVAL;
		}

		obj->maps[map_idx].name = strdup(map_name);
		if (!obj->maps[map_idx].name) {
			pr_warning("failed to alloc map name\n");
			return -ENOMEM;
		}
		pr_debug("map %zu is \"%s\"\n", map_idx,
		pr_debug("map %d is \"%s\"\n", map_idx,
			 obj->maps[map_idx].name);
		def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
		obj->maps[map_idx].def = *def;
		map_idx++;
	}
	return 0;

	qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), compare_bpf_map);
	return bpf_object__validate_maps(obj);
}

static int bpf_object__elf_collect(struct bpf_object *obj)
@@ -645,11 +701,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
			err = bpf_object__init_kversion(obj,
							data->d_buf,
							data->d_size);
		else if (strcmp(name, "maps") == 0) {
			err = bpf_object__init_maps(obj, data->d_buf,
						    data->d_size);
		else if (strcmp(name, "maps") == 0)
			obj->efile.maps_shndx = idx;
		} else if (sh.sh_type == SHT_SYMTAB) {
		else if (sh.sh_type == SHT_SYMTAB) {
			if (obj->efile.symbols) {
				pr_warning("bpf: multiple SYMTAB in %s\n",
					   obj->path);
@@ -698,7 +752,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
		return LIBBPF_ERRNO__FORMAT;
	}
	if (obj->efile.maps_shndx >= 0)
		err = bpf_object__init_maps_name(obj);
		err = bpf_object__init_maps(obj);
out:
	return err;
}
@@ -807,7 +861,7 @@ bpf_object__create_maps(struct bpf_object *obj)
				zclose(obj->maps[j].fd);
			return err;
		}
		pr_debug("create map: fd=%d\n", *pfd);
		pr_debug("create map %s: fd=%d\n", obj->maps[i].name, *pfd);
	}

	return 0;
+58 −89
Original line number Diff line number Diff line
static struct ins arm__instructions[] = {
	{ .name = "add",	.ops = &mov_ops,  },
	{ .name = "addl",	.ops = &mov_ops,  },
	{ .name = "addq",	.ops = &mov_ops,  },
	{ .name = "addw",	.ops = &mov_ops,  },
	{ .name = "and",	.ops = &mov_ops,  },
	{ .name = "b",		.ops = &jump_ops, }, // might also be a call
	{ .name = "bcc",	.ops = &jump_ops, },
	{ .name = "bcs",	.ops = &jump_ops, },
	{ .name = "beq",	.ops = &jump_ops, },
	{ .name = "bge",	.ops = &jump_ops, },
	{ .name = "bgt",	.ops = &jump_ops, },
	{ .name = "bhi",	.ops = &jump_ops, },
	{ .name = "bl",		.ops = &call_ops, },
	{ .name = "bls",	.ops = &jump_ops, },
	{ .name = "blt",	.ops = &jump_ops, },
	{ .name = "blx",	.ops = &call_ops, },
	{ .name = "bne",	.ops = &jump_ops, },
	{ .name = "bts",	.ops = &mov_ops,  },
	{ .name = "call",	.ops = &call_ops, },
	{ .name = "callq",	.ops = &call_ops, },
	{ .name = "cmp",	.ops = &mov_ops,  },
	{ .name = "cmpb",	.ops = &mov_ops,  },
	{ .name = "cmpl",	.ops = &mov_ops,  },
	{ .name = "cmpq",	.ops = &mov_ops,  },
	{ .name = "cmpw",	.ops = &mov_ops,  },
	{ .name = "cmpxch",	.ops = &mov_ops,  },
	{ .name = "dec",	.ops = &dec_ops,  },
	{ .name = "decl",	.ops = &dec_ops,  },
	{ .name = "imul",	.ops = &mov_ops,  },
	{ .name = "inc",	.ops = &dec_ops,  },
	{ .name = "incl",	.ops = &dec_ops,  },
	{ .name = "ja",		.ops = &jump_ops, },
	{ .name = "jae",	.ops = &jump_ops, },
	{ .name = "jb",		.ops = &jump_ops, },
	{ .name = "jbe",	.ops = &jump_ops, },
	{ .name = "jc",		.ops = &jump_ops, },
	{ .name = "jcxz",	.ops = &jump_ops, },
	{ .name = "je",		.ops = &jump_ops, },
	{ .name = "jecxz",	.ops = &jump_ops, },
	{ .name = "jg",		.ops = &jump_ops, },
	{ .name = "jge",	.ops = &jump_ops, },
	{ .name = "jl",		.ops = &jump_ops, },
	{ .name = "jle",	.ops = &jump_ops, },
	{ .name = "jmp",	.ops = &jump_ops, },
	{ .name = "jmpq",	.ops = &jump_ops, },
	{ .name = "jna",	.ops = &jump_ops, },
	{ .name = "jnae",	.ops = &jump_ops, },
	{ .name = "jnb",	.ops = &jump_ops, },
	{ .name = "jnbe",	.ops = &jump_ops, },
	{ .name = "jnc",	.ops = &jump_ops, },
	{ .name = "jne",	.ops = &jump_ops, },
	{ .name = "jng",	.ops = &jump_ops, },
	{ .name = "jnge",	.ops = &jump_ops, },
	{ .name = "jnl",	.ops = &jump_ops, },
	{ .name = "jnle",	.ops = &jump_ops, },
	{ .name = "jno",	.ops = &jump_ops, },
	{ .name = "jnp",	.ops = &jump_ops, },
	{ .name = "jns",	.ops = &jump_ops, },
	{ .name = "jnz",	.ops = &jump_ops, },
	{ .name = "jo",		.ops = &jump_ops, },
	{ .name = "jp",		.ops = &jump_ops, },
	{ .name = "jpe",	.ops = &jump_ops, },
	{ .name = "jpo",	.ops = &jump_ops, },
	{ .name = "jrcxz",	.ops = &jump_ops, },
	{ .name = "js",		.ops = &jump_ops, },
	{ .name = "jz",		.ops = &jump_ops, },
	{ .name = "lea",	.ops = &mov_ops,  },
	{ .name = "lock",	.ops = &lock_ops, },
	{ .name = "mov",	.ops = &mov_ops,  },
	{ .name = "movb",	.ops = &mov_ops,  },
	{ .name = "movdqa",	.ops = &mov_ops,  },
	{ .name = "movl",	.ops = &mov_ops,  },
	{ .name = "movq",	.ops = &mov_ops,  },
	{ .name = "movslq",	.ops = &mov_ops,  },
	{ .name = "movzbl",	.ops = &mov_ops,  },
	{ .name = "movzwl",	.ops = &mov_ops,  },
	{ .name = "nop",	.ops = &nop_ops,  },
	{ .name = "nopl",	.ops = &nop_ops,  },
	{ .name = "nopw",	.ops = &nop_ops,  },
	{ .name = "or",		.ops = &mov_ops,  },
	{ .name = "orl",	.ops = &mov_ops,  },
	{ .name = "test",	.ops = &mov_ops,  },
	{ .name = "testb",	.ops = &mov_ops,  },
	{ .name = "testl",	.ops = &mov_ops,  },
	{ .name = "xadd",	.ops = &mov_ops,  },
	{ .name = "xbeginl",	.ops = &jump_ops, },
	{ .name = "xbeginq",	.ops = &jump_ops, },
	{ .name = "retq",	.ops = &ret_ops,  },
#include <sys/types.h>
#include <regex.h>

struct arm_annotate {
	regex_t call_insn,
		jump_insn;
};

static struct ins_ops *arm__associate_instruction_ops(struct arch *arch, const char *name)
{
	struct arm_annotate *arm = arch->priv;
	struct ins_ops *ops;
	regmatch_t match[2];

	if (!regexec(&arm->call_insn, name, 2, match, 0))
		ops = &call_ops;
	else if (!regexec(&arm->jump_insn, name, 2, match, 0))
		ops = &jump_ops;
	else
		return NULL;

	arch__associate_ins_ops(arch, name, ops);
	return ops;
}

static int arm__annotate_init(struct arch *arch)
{
	struct arm_annotate *arm;
	int err;

	if (arch->initialized)
		return 0;

	arm = zalloc(sizeof(*arm));
	if (!arm)
		return -1;

#define ARM_CONDS "(cc|cs|eq|ge|gt|hi|le|ls|lt|mi|ne|pl|vc|vs)"
	err = regcomp(&arm->call_insn, "^blx?" ARM_CONDS "?$", REG_EXTENDED);
	if (err)
		goto out_free_arm;
	err = regcomp(&arm->jump_insn, "^bx?" ARM_CONDS "?$", REG_EXTENDED);
	if (err)
		goto out_free_call;
#undef ARM_CONDS

	arch->initialized = true;
	arch->priv	  = arm;
	arch->associate_instruction_ops   = arm__associate_instruction_ops;
	arch->objdump.comment_char	  = ';';
	arch->objdump.skip_functions_char = '+';
	return 0;

out_free_call:
	regfree(&arm->call_insn);
out_free_arm:
	free(arm);
	return -1;
}
+58 −0
Original line number Diff line number Diff line
static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, const char *name)
{
	int i;
	struct ins_ops *ops;

	/*
	 * - Interested only if instruction starts with 'b'.
	 * - Few start with 'b', but aren't branch instructions.
	 */
	if (name[0] != 'b'             ||
	    !strncmp(name, "bcd", 3)   ||
	    !strncmp(name, "brinc", 5) ||
	    !strncmp(name, "bper", 4))
		return NULL;

	ops = &jump_ops;

	i = strlen(name) - 1;
	if (i < 0)
		return NULL;

	/* ignore optional hints at the end of the instructions */
	if (name[i] == '+' || name[i] == '-')
		i--;

	if (name[i] == 'l' || (name[i] == 'a' && name[i-1] == 'l')) {
		/*
		 * if the instruction ends up with 'l' or 'la', then
		 * those are considered 'calls' since they update LR.
		 * ... except for 'bnl' which is branch if not less than
		 * and the absolute form of the same.
		 */
		if (strcmp(name, "bnl") && strcmp(name, "bnl+") &&
		    strcmp(name, "bnl-") && strcmp(name, "bnla") &&
		    strcmp(name, "bnla+") && strcmp(name, "bnla-"))
			ops = &call_ops;
	}
	if (name[i] == 'r' && name[i-1] == 'l')
		/*
		 * instructions ending with 'lr' are considered to be
		 * return instructions
		 */
		ops = &ret_ops;

	arch__associate_ins_ops(arch, name, ops);
	return ops;
}

static int powerpc__annotate_init(struct arch *arch)
{
	if (!arch->initialized) {
		arch->initialized = true;
		arch->associate_instruction_ops = powerpc__associate_instruction_ops;
		arch->objdump.comment_char      = '#';
	}

	return 0;
}
+24 −2
Original line number Diff line number Diff line
@@ -1876,7 +1876,8 @@ static void timehist_print_sample(struct perf_sched *sched,

	sample__fprintf_sym(sample, al, 0,
			    EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
			    EVSEL__PRINT_CALLCHAIN_ARROW,
			    EVSEL__PRINT_CALLCHAIN_ARROW |
			    EVSEL__PRINT_SKIP_IGNORED,
			    &callchain_cursor, stdout);

out:
@@ -1959,13 +1960,34 @@ static bool is_idle_sample(struct perf_sched *sched,
		return false;

	if (thread__resolve_callchain(thread, cursor, evsel, sample,
				      NULL, NULL, sched->max_stack) != 0) {
				      NULL, NULL, sched->max_stack + 2) != 0) {
		if (verbose)
			error("Failed to resolve callchain. Skipping\n");

		return false;
	}

	callchain_cursor_commit(cursor);

	while (true) {
		struct callchain_cursor_node *node;
		struct symbol *sym;

		node = callchain_cursor_current(cursor);
		if (node == NULL)
			break;

		sym = node->sym;
		if (sym && sym->name) {
			if (!strcmp(sym->name, "schedule") ||
			    !strcmp(sym->name, "__schedule") ||
			    !strcmp(sym->name, "preempt_schedule"))
				sym->ignore = 1;
		}

		callchain_cursor_advance(cursor);
	}

	return false;
}

+9 −9
Original line number Diff line number Diff line
@@ -213,17 +213,17 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
		ui_browser__write_nstring(browser, bf, printed);
		if (change_color)
			ui_browser__set_color(browser, color);
		if (dl->ins && dl->ins->ops->scnprintf) {
			if (ins__is_jump(dl->ins)) {
		if (dl->ins.ops && dl->ins.ops->scnprintf) {
			if (ins__is_jump(&dl->ins)) {
				bool fwd = dl->ops.target.offset > (u64)dl->offset;

				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
								    SLSMG_UARROW_CHAR);
				SLsmg_write_char(' ');
			} else if (ins__is_call(dl->ins)) {
			} else if (ins__is_call(&dl->ins)) {
				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
				SLsmg_write_char(' ');
			} else if (ins__is_ret(dl->ins)) {
			} else if (ins__is_ret(&dl->ins)) {
				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
				SLsmg_write_char(' ');
			} else {
@@ -243,7 +243,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int

static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym)
{
	if (!dl || !dl->ins || !ins__is_jump(dl->ins)
	if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins)
	    || !disasm_line__has_offset(dl)
	    || dl->ops.target.offset >= symbol__size(sym))
		return false;
@@ -492,7 +492,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
	};
	char title[SYM_TITLE_MAX_SIZE];

	if (!ins__is_call(dl->ins))
	if (!ins__is_call(&dl->ins))
		return false;

	if (map_groups__find_ams(&target) ||
@@ -545,7 +545,7 @@ static bool annotate_browser__jump(struct annotate_browser *browser)
	struct disasm_line *dl = browser->selection;
	s64 idx;

	if (!ins__is_jump(dl->ins))
	if (!ins__is_jump(&dl->ins))
		return false;

	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
@@ -841,9 +841,9 @@ static int annotate_browser__run(struct annotate_browser *browser,
				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
			else if (browser->selection->offset == -1)
				ui_helpline__puts("Actions are only available for assembly lines.");
			else if (!browser->selection->ins)
			else if (!browser->selection->ins.ops)
				goto show_sup_ins;
			else if (ins__is_ret(browser->selection->ins))
			else if (ins__is_ret(&browser->selection->ins))
				goto out;
			else if (!(annotate_browser__jump(browser) ||
				     annotate_browser__callq(browser, evsel, hbt))) {
Loading