Browse Source

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: Arnaldo Carvalho de Melo <acme@redhat.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Ingo Molnar 8 years ago
parent
commit
2471cece40

+ 98 - 44
tools/lib/bpf/libbpf.c

@@ -185,6 +185,7 @@ struct bpf_program {
 struct bpf_map {
 struct bpf_map {
 	int fd;
 	int fd;
 	char *name;
 	char *name;
+	size_t offset;
 	struct bpf_map_def def;
 	struct bpf_map_def def;
 	void *priv;
 	void *priv;
 	bpf_map_clear_priv_t clear_priv;
 	bpf_map_clear_priv_t clear_priv;
@@ -513,57 +514,106 @@ bpf_object__init_kversion(struct bpf_object *obj,
 }
 }
 
 
 static int
 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;
 	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;
 		return 0;
-	}
 
 
-	pr_debug("maps in %s: %zd bytes\n", obj->path, size);
+	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];
 
 
-	obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
-	if (!obj->maps) {
-		pr_warning("alloc maps for object failed\n");
-		return -ENOMEM;
+		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;
+		}
 	}
 	}
-	obj->nr_maps = nr_maps;
-
-	for (i = 0; i < nr_maps; i++) {
-		struct bpf_map_def *def = &obj->maps[i].def;
+	return 0;
+}
 
 
-		/*
-		 * fill all fd with -1 so won't close incorrect
-		 * fd (fd=0 is stdin) when failure (zclose won't close
-		 * negative fd)).
-		 */
-		obj->maps[i].fd = -1;
+static int compare_bpf_map(const void *_a, const void *_b)
+{
+	const struct bpf_map *a = _a;
+	const struct bpf_map *b = _b;
 
 
-		/* Save map definition into obj->maps */
-		*def = ((struct bpf_map_def *)data)[i];
-	}
-	return 0;
+	return a->offset - b->offset;
 }
 }
 
 
 static int
 static int
-bpf_object__init_maps_name(struct bpf_object *obj)
+bpf_object__init_maps(struct bpf_object *obj)
 {
 {
-	int i;
+	int i, map_idx, nr_maps = 0;
+	Elf_Scn *scn;
+	Elf_Data *data;
 	Elf_Data *symbols = obj->efile.symbols;
 	Elf_Data *symbols = obj->efile.symbols;
 
 
-	if (!symbols || obj->efile.maps_shndx < 0)
+	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;
 		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++) {
 	for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
 		GElf_Sym sym;
 		GElf_Sym sym;
-		size_t map_idx;
+
+		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) {
+		pr_warning("alloc maps for object failed\n");
+		return -ENOMEM;
+	}
+	obj->nr_maps = nr_maps;
+
+	/*
+	 * 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;
+
+	/*
+	 * 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;
 		const char *map_name;
 		const char *map_name;
+		struct bpf_map_def *def;
 
 
 		if (!gelf_getsym(symbols, i, &sym))
 		if (!gelf_getsym(symbols, i, &sym))
 			continue;
 			continue;
@@ -573,21 +623,27 @@ bpf_object__init_maps_name(struct bpf_object *obj)
 		map_name = elf_strptr(obj->efile.elf,
 		map_name = elf_strptr(obj->efile.elf,
 				      obj->efile.strtabidx,
 				      obj->efile.strtabidx,
 				      sym.st_name);
 				      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);
 		obj->maps[map_idx].name = strdup(map_name);
 		if (!obj->maps[map_idx].name) {
 		if (!obj->maps[map_idx].name) {
 			pr_warning("failed to alloc map name\n");
 			pr_warning("failed to alloc map name\n");
 			return -ENOMEM;
 			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);
 			 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)
 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,
 			err = bpf_object__init_kversion(obj,
 							data->d_buf,
 							data->d_buf,
 							data->d_size);
 							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;
 			obj->efile.maps_shndx = idx;
-		} else if (sh.sh_type == SHT_SYMTAB) {
+		else if (sh.sh_type == SHT_SYMTAB) {
 			if (obj->efile.symbols) {
 			if (obj->efile.symbols) {
 				pr_warning("bpf: multiple SYMTAB in %s\n",
 				pr_warning("bpf: multiple SYMTAB in %s\n",
 					   obj->path);
 					   obj->path);
@@ -698,7 +752,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj)
 		return LIBBPF_ERRNO__FORMAT;
 		return LIBBPF_ERRNO__FORMAT;
 	}
 	}
 	if (obj->efile.maps_shndx >= 0)
 	if (obj->efile.maps_shndx >= 0)
-		err = bpf_object__init_maps_name(obj);
+		err = bpf_object__init_maps(obj);
 out:
 out:
 	return err;
 	return err;
 }
 }
@@ -807,7 +861,7 @@ bpf_object__create_maps(struct bpf_object *obj)
 				zclose(obj->maps[j].fd);
 				zclose(obj->maps[j].fd);
 			return err;
 			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;
 	return 0;

+ 58 - 89
tools/perf/arch/arm/annotate/instructions.c

@@ -1,90 +1,59 @@
-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
tools/perf/arch/powerpc/annotate/instructions.c

@@ -0,0 +1,58 @@
+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
tools/perf/builtin-sched.c

@@ -1876,7 +1876,8 @@ static void timehist_print_sample(struct perf_sched *sched,
 
 
 	sample__fprintf_sym(sample, al, 0,
 	sample__fprintf_sym(sample, al, 0,
 			    EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
 			    EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE |
-			    EVSEL__PRINT_CALLCHAIN_ARROW,
+			    EVSEL__PRINT_CALLCHAIN_ARROW |
+			    EVSEL__PRINT_SKIP_IGNORED,
 			    &callchain_cursor, stdout);
 			    &callchain_cursor, stdout);
 
 
 out:
 out:
@@ -1959,13 +1960,34 @@ static bool is_idle_sample(struct perf_sched *sched,
 		return false;
 		return false;
 
 
 	if (thread__resolve_callchain(thread, cursor, evsel, sample,
 	if (thread__resolve_callchain(thread, cursor, evsel, sample,
-				      NULL, NULL, sched->max_stack) != 0) {
+				      NULL, NULL, sched->max_stack + 2) != 0) {
 		if (verbose)
 		if (verbose)
 			error("Failed to resolve callchain. Skipping\n");
 			error("Failed to resolve callchain. Skipping\n");
 
 
 		return false;
 		return false;
 	}
 	}
+
 	callchain_cursor_commit(cursor);
 	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;
 	return false;
 }
 }
 
 

+ 9 - 9
tools/perf/ui/browsers/annotate.c

@@ -213,17 +213,17 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int
 		ui_browser__write_nstring(browser, bf, printed);
 		ui_browser__write_nstring(browser, bf, printed);
 		if (change_color)
 		if (change_color)
 			ui_browser__set_color(browser, 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;
 				bool fwd = dl->ops.target.offset > (u64)dl->offset;
 
 
 				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
 				ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR :
 								    SLSMG_UARROW_CHAR);
 								    SLSMG_UARROW_CHAR);
 				SLsmg_write_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);
 				ui_browser__write_graph(browser, SLSMG_RARROW_CHAR);
 				SLsmg_write_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);
 				ui_browser__write_graph(browser, SLSMG_LARROW_CHAR);
 				SLsmg_write_char(' ');
 				SLsmg_write_char(' ');
 			} else {
 			} 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)
 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)
 	    || !disasm_line__has_offset(dl)
 	    || dl->ops.target.offset >= symbol__size(sym))
 	    || dl->ops.target.offset >= symbol__size(sym))
 		return false;
 		return false;
@@ -492,7 +492,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser,
 	};
 	};
 	char title[SYM_TITLE_MAX_SIZE];
 	char title[SYM_TITLE_MAX_SIZE];
 
 
-	if (!ins__is_call(dl->ins))
+	if (!ins__is_call(&dl->ins))
 		return false;
 		return false;
 
 
 	if (map_groups__find_ams(&target) ||
 	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;
 	struct disasm_line *dl = browser->selection;
 	s64 idx;
 	s64 idx;
 
 
-	if (!ins__is_jump(dl->ins))
+	if (!ins__is_jump(&dl->ins))
 		return false;
 		return false;
 
 
 	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
 	dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx);
@@ -841,9 +841,9 @@ show_help:
 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
 				ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org");
 			else if (browser->selection->offset == -1)
 			else if (browser->selection->offset == -1)
 				ui_helpline__puts("Actions are only available for assembly lines.");
 				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;
 				goto show_sup_ins;
-			else if (ins__is_ret(browser->selection->ins))
+			else if (ins__is_ret(&browser->selection->ins))
 				goto out;
 				goto out;
 			else if (!(annotate_browser__jump(browser) ||
 			else if (!(annotate_browser__jump(browser) ||
 				     annotate_browser__callq(browser, evsel, hbt))) {
 				     annotate_browser__callq(browser, evsel, hbt))) {

+ 112 - 45
tools/perf/util/annotate.c

@@ -28,14 +28,20 @@ const char 	*disassembler_style;
 const char	*objdump_path;
 const char	*objdump_path;
 static regex_t	 file_lineno;
 static regex_t	 file_lineno;
 
 
-static struct ins *ins__find(struct arch *arch, const char *name);
-static int disasm_line__parse(char *line, char **namep, char **rawp);
+static struct ins_ops *ins__find(struct arch *arch, const char *name);
+static void ins__sort(struct arch *arch);
+static int disasm_line__parse(char *line, const char **namep, char **rawp);
 
 
 struct arch {
 struct arch {
 	const char	*name;
 	const char	*name;
 	struct ins	*instructions;
 	struct ins	*instructions;
 	size_t		nr_instructions;
 	size_t		nr_instructions;
+	size_t		nr_instructions_allocated;
+	struct ins_ops  *(*associate_instruction_ops)(struct arch *arch, const char *name);
 	bool		sorted_instructions;
 	bool		sorted_instructions;
+	bool		initialized;
+	void		*priv;
+	int		(*init)(struct arch *arch);
 	struct		{
 	struct		{
 		char comment_char;
 		char comment_char;
 		char skip_functions_char;
 		char skip_functions_char;
@@ -50,18 +56,62 @@ static struct ins_ops nop_ops;
 static struct ins_ops lock_ops;
 static struct ins_ops lock_ops;
 static struct ins_ops ret_ops;
 static struct ins_ops ret_ops;
 
 
+static int arch__grow_instructions(struct arch *arch)
+{
+	struct ins *new_instructions;
+	size_t new_nr_allocated;
+
+	if (arch->nr_instructions_allocated == 0 && arch->instructions)
+		goto grow_from_non_allocated_table;
+
+	new_nr_allocated = arch->nr_instructions_allocated + 128;
+	new_instructions = realloc(arch->instructions, new_nr_allocated * sizeof(struct ins));
+	if (new_instructions == NULL)
+		return -1;
+
+out_update_instructions:
+	arch->instructions = new_instructions;
+	arch->nr_instructions_allocated = new_nr_allocated;
+	return 0;
+
+grow_from_non_allocated_table:
+	new_nr_allocated = arch->nr_instructions + 128;
+	new_instructions = calloc(new_nr_allocated, sizeof(struct ins));
+	if (new_instructions == NULL)
+		return -1;
+
+	memcpy(new_instructions, arch->instructions, arch->nr_instructions);
+	goto out_update_instructions;
+}
+
+static int arch__associate_ins_ops(struct arch* arch, const char *name, struct ins_ops *ops)
+{
+	struct ins *ins;
+
+	if (arch->nr_instructions == arch->nr_instructions_allocated &&
+	    arch__grow_instructions(arch))
+		return -1;
+
+	ins = &arch->instructions[arch->nr_instructions];
+	ins->name = strdup(name);
+	if (!ins->name)
+		return -1;
+
+	ins->ops  = ops;
+	arch->nr_instructions++;
+
+	ins__sort(arch);
+	return 0;
+}
+
 #include "arch/arm/annotate/instructions.c"
 #include "arch/arm/annotate/instructions.c"
 #include "arch/x86/annotate/instructions.c"
 #include "arch/x86/annotate/instructions.c"
+#include "arch/powerpc/annotate/instructions.c"
 
 
 static struct arch architectures[] = {
 static struct arch architectures[] = {
 	{
 	{
 		.name = "arm",
 		.name = "arm",
-		.instructions = arm__instructions,
-		.nr_instructions = ARRAY_SIZE(arm__instructions),
-		.objdump =  {
-			.comment_char = ';',
-			.skip_functions_char = '+',
-		},
+		.init = arm__annotate_init,
 	},
 	},
 	{
 	{
 		.name = "x86",
 		.name = "x86",
@@ -71,6 +121,10 @@ static struct arch architectures[] = {
 			.comment_char = '#',
 			.comment_char = '#',
 		},
 		},
 	},
 	},
+	{
+		.name = "powerpc",
+		.init = powerpc__annotate_init,
+	},
 };
 };
 
 
 static void ins__delete(struct ins_operands *ops)
 static void ins__delete(struct ins_operands *ops)
@@ -218,26 +272,20 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)
 
 
 static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map)
 static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map)
 {
 {
-	char *name;
-
 	ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
 	ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
 	if (ops->locked.ops == NULL)
 	if (ops->locked.ops == NULL)
 		return 0;
 		return 0;
 
 
-	if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0)
+	if (disasm_line__parse(ops->raw, &ops->locked.ins.name, &ops->locked.ops->raw) < 0)
 		goto out_free_ops;
 		goto out_free_ops;
 
 
-	ops->locked.ins = ins__find(arch, name);
-	free(name);
+	ops->locked.ins.ops = ins__find(arch, ops->locked.ins.name);
 
 
-	if (ops->locked.ins == NULL)
+	if (ops->locked.ins.ops == NULL)
 		goto out_free_ops;
 		goto out_free_ops;
 
 
-	if (!ops->locked.ins->ops)
-		return 0;
-
-	if (ops->locked.ins->ops->parse &&
-	    ops->locked.ins->ops->parse(arch, ops->locked.ops, map) < 0)
+	if (ops->locked.ins.ops->parse &&
+	    ops->locked.ins.ops->parse(arch, ops->locked.ops, map) < 0)
 		goto out_free_ops;
 		goto out_free_ops;
 
 
 	return 0;
 	return 0;
@@ -252,19 +300,19 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size,
 {
 {
 	int printed;
 	int printed;
 
 
-	if (ops->locked.ins == NULL)
+	if (ops->locked.ins.ops == NULL)
 		return ins__raw_scnprintf(ins, bf, size, ops);
 		return ins__raw_scnprintf(ins, bf, size, ops);
 
 
 	printed = scnprintf(bf, size, "%-6.6s ", ins->name);
 	printed = scnprintf(bf, size, "%-6.6s ", ins->name);
-	return printed + ins__scnprintf(ops->locked.ins, bf + printed,
+	return printed + ins__scnprintf(&ops->locked.ins, bf + printed,
 					size - printed, ops->locked.ops);
 					size - printed, ops->locked.ops);
 }
 }
 
 
 static void lock__delete(struct ins_operands *ops)
 static void lock__delete(struct ins_operands *ops)
 {
 {
-	struct ins *ins = ops->locked.ins;
+	struct ins *ins = &ops->locked.ins;
 
 
-	if (ins && ins->ops->free)
+	if (ins->ops && ins->ops->free)
 		ins->ops->free(ops->locked.ops);
 		ins->ops->free(ops->locked.ops);
 	else
 	else
 		ins__delete(ops->locked.ops);
 		ins__delete(ops->locked.ops);
@@ -425,8 +473,9 @@ static void ins__sort(struct arch *arch)
 	qsort(arch->instructions, nmemb, sizeof(struct ins), ins__cmp);
 	qsort(arch->instructions, nmemb, sizeof(struct ins), ins__cmp);
 }
 }
 
 
-static struct ins *ins__find(struct arch *arch, const char *name)
+static struct ins_ops *__ins__find(struct arch *arch, const char *name)
 {
 {
+	struct ins *ins;
 	const int nmemb = arch->nr_instructions;
 	const int nmemb = arch->nr_instructions;
 
 
 	if (!arch->sorted_instructions) {
 	if (!arch->sorted_instructions) {
@@ -434,7 +483,18 @@ static struct ins *ins__find(struct arch *arch, const char *name)
 		arch->sorted_instructions = true;
 		arch->sorted_instructions = true;
 	}
 	}
 
 
-	return bsearch(name, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp);
+	ins = bsearch(name, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp);
+	return ins ? ins->ops : NULL;
+}
+
+static struct ins_ops *ins__find(struct arch *arch, const char *name)
+{
+	struct ins_ops *ops = __ins__find(arch, name);
+
+	if (!ops && arch->associate_instruction_ops)
+		ops = arch->associate_instruction_ops(arch, name);
+
+	return ops;
 }
 }
 
 
 static int arch__key_cmp(const void *name, const void *archp)
 static int arch__key_cmp(const void *name, const void *archp)
@@ -691,19 +751,16 @@ int hist_entry__inc_addr_samples(struct hist_entry *he, int evidx, u64 ip)
 
 
 static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map)
 static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map)
 {
 {
-	dl->ins = ins__find(arch, dl->name);
+	dl->ins.ops = ins__find(arch, dl->ins.name);
 
 
-	if (dl->ins == NULL)
+	if (!dl->ins.ops)
 		return;
 		return;
 
 
-	if (!dl->ins->ops)
-		return;
-
-	if (dl->ins->ops->parse && dl->ins->ops->parse(arch, &dl->ops, map) < 0)
-		dl->ins = NULL;
+	if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, map) < 0)
+		dl->ins.ops = NULL;
 }
 }
 
 
-static int disasm_line__parse(char *line, char **namep, char **rawp)
+static int disasm_line__parse(char *line, const char **namep, char **rawp)
 {
 {
 	char *name = line, tmp;
 	char *name = line, tmp;
 
 
@@ -736,7 +793,8 @@ static int disasm_line__parse(char *line, char **namep, char **rawp)
 	return 0;
 	return 0;
 
 
 out_free_name:
 out_free_name:
-	zfree(namep);
+	free((void *)namep);
+	*namep = NULL;
 	return -1;
 	return -1;
 }
 }
 
 
@@ -755,7 +813,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line,
 			goto out_delete;
 			goto out_delete;
 
 
 		if (offset != -1) {
 		if (offset != -1) {
-			if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0)
+			if (disasm_line__parse(dl->line, &dl->ins.name, &dl->ops.raw) < 0)
 				goto out_free_line;
 				goto out_free_line;
 
 
 			disasm_line__init_ins(dl, arch, map);
 			disasm_line__init_ins(dl, arch, map);
@@ -774,20 +832,21 @@ out_delete:
 void disasm_line__free(struct disasm_line *dl)
 void disasm_line__free(struct disasm_line *dl)
 {
 {
 	zfree(&dl->line);
 	zfree(&dl->line);
-	zfree(&dl->name);
-	if (dl->ins && dl->ins->ops->free)
-		dl->ins->ops->free(&dl->ops);
+	if (dl->ins.ops && dl->ins.ops->free)
+		dl->ins.ops->free(&dl->ops);
 	else
 	else
 		ins__delete(&dl->ops);
 		ins__delete(&dl->ops);
+	free((void *)dl->ins.name);
+	dl->ins.name = NULL;
 	free(dl);
 	free(dl);
 }
 }
 
 
 int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw)
 int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw)
 {
 {
-	if (raw || !dl->ins)
-		return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw);
+	if (raw || !dl->ins.ops)
+		return scnprintf(bf, size, "%-6.6s %s", dl->ins.name, dl->ops.raw);
 
 
-	return ins__scnprintf(dl->ins, bf, size, &dl->ops);
+	return ins__scnprintf(&dl->ins, bf, size, &dl->ops);
 }
 }
 
 
 static void disasm__add(struct list_head *head, struct disasm_line *line)
 static void disasm__add(struct list_head *head, struct disasm_line *line)
@@ -1143,7 +1202,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
 					map__rip_2objdump(map, sym->start);
 					map__rip_2objdump(map, sym->start);
 
 
 	/* kcore has no symbols, so add the call target name */
 	/* kcore has no symbols, so add the call target name */
-	if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) {
+	if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.name) {
 		struct addr_map_symbol target = {
 		struct addr_map_symbol target = {
 			.map = map,
 			.map = map,
 			.addr = dl->ops.target.addr,
 			.addr = dl->ops.target.addr,
@@ -1173,8 +1232,8 @@ static void delete_last_nop(struct symbol *sym)
 	while (!list_empty(list)) {
 	while (!list_empty(list)) {
 		dl = list_entry(list->prev, struct disasm_line, node);
 		dl = list_entry(list->prev, struct disasm_line, node);
 
 
-		if (dl->ins && dl->ins->ops) {
-			if (dl->ins->ops != &nop_ops)
+		if (dl->ins.ops) {
+			if (dl->ins.ops != &nop_ops)
 				return;
 				return;
 		} else {
 		} else {
 			if (!strstr(dl->line, " nop ") &&
 			if (!strstr(dl->line, " nop ") &&
@@ -1300,6 +1359,14 @@ int symbol__disassemble(struct symbol *sym, struct map *map, const char *arch_na
 	if (arch == NULL)
 	if (arch == NULL)
 		return -ENOTSUP;
 		return -ENOTSUP;
 
 
+	if (arch->init) {
+		err = arch->init(arch);
+		if (err) {
+			pr_err("%s: failed to initialize %s arch priv area\n", __func__, arch->name);
+			return err;
+		}
+	}
+
 	pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
 	pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__,
 		 symfs_filename, sym->name, map->unmap_ip(map, sym->start),
 		 symfs_filename, sym->name, map->unmap_ip(map, sym->start),
 		 map->unmap_ip(map, sym->end));
 		 map->unmap_ip(map, sym->end));
@@ -1767,7 +1834,7 @@ static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp)
 	if (dl->offset == -1)
 	if (dl->offset == -1)
 		return fprintf(fp, "%s\n", dl->line);
 		return fprintf(fp, "%s\n", dl->line);
 
 
-	printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name);
+	printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->ins.name);
 
 
 	if (dl->ops.raw[0] != '\0') {
 	if (dl->ops.raw[0] != '\0') {
 		printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ",
 		printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ",

+ 8 - 9
tools/perf/util/annotate.h

@@ -11,7 +11,12 @@
 #include <linux/rbtree.h>
 #include <linux/rbtree.h>
 #include <pthread.h>
 #include <pthread.h>
 
 
-struct ins;
+struct ins_ops;
+
+struct ins {
+	const char     *name;
+	struct ins_ops *ops;
+};
 
 
 struct ins_operands {
 struct ins_operands {
 	char	*raw;
 	char	*raw;
@@ -28,7 +33,7 @@ struct ins_operands {
 			u64	addr;
 			u64	addr;
 		} source;
 		} source;
 		struct {
 		struct {
-			struct ins *ins;
+			struct ins	    ins;
 			struct ins_operands *ops;
 			struct ins_operands *ops;
 		} locked;
 		} locked;
 	};
 	};
@@ -43,11 +48,6 @@ struct ins_ops {
 			 struct ins_operands *ops);
 			 struct ins_operands *ops);
 };
 };
 
 
-struct ins {
-	const char     *name;
-	struct ins_ops *ops;
-};
-
 bool ins__is_jump(const struct ins *ins);
 bool ins__is_jump(const struct ins *ins);
 bool ins__is_call(const struct ins *ins);
 bool ins__is_call(const struct ins *ins);
 bool ins__is_ret(const struct ins *ins);
 bool ins__is_ret(const struct ins *ins);
@@ -59,8 +59,7 @@ struct disasm_line {
 	struct list_head    node;
 	struct list_head    node;
 	s64		    offset;
 	s64		    offset;
 	char		    *line;
 	char		    *line;
-	char		    *name;
-	struct ins	    *ins;
+	struct ins	    ins;
 	int		    line_nr;
 	int		    line_nr;
 	float		    ipc;
 	float		    ipc;
 	u64		    cycles;
 	u64		    cycles;

+ 1 - 0
tools/perf/util/evsel.h

@@ -392,6 +392,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
 #define EVSEL__PRINT_SRCLINE		(1<<5)
 #define EVSEL__PRINT_SRCLINE		(1<<5)
 #define EVSEL__PRINT_UNKNOWN_AS_ADDR	(1<<6)
 #define EVSEL__PRINT_UNKNOWN_AS_ADDR	(1<<6)
 #define EVSEL__PRINT_CALLCHAIN_ARROW	(1<<7)
 #define EVSEL__PRINT_CALLCHAIN_ARROW	(1<<7)
+#define EVSEL__PRINT_SKIP_IGNORED	(1<<8)
 
 
 struct callchain_cursor;
 struct callchain_cursor;
 
 

+ 6 - 1
tools/perf/util/evsel_fprintf.c

@@ -109,6 +109,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
 	int print_srcline = print_opts & EVSEL__PRINT_SRCLINE;
 	int print_srcline = print_opts & EVSEL__PRINT_SRCLINE;
 	int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR;
 	int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR;
 	int print_arrow = print_opts & EVSEL__PRINT_CALLCHAIN_ARROW;
 	int print_arrow = print_opts & EVSEL__PRINT_CALLCHAIN_ARROW;
+	int print_skip_ignored = print_opts & EVSEL__PRINT_SKIP_IGNORED;
 	char s = print_oneline ? ' ' : '\t';
 	char s = print_oneline ? ' ' : '\t';
 	bool first = true;
 	bool first = true;
 
 
@@ -124,6 +125,9 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
 			if (!node)
 			if (!node)
 				break;
 				break;
 
 
+			if (node->sym && node->sym->ignore && print_skip_ignored)
+				goto next;
+
 			printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
 			printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
 
 
 			if (print_arrow && !first)
 			if (print_arrow && !first)
@@ -162,8 +166,9 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
 			if (!print_oneline)
 			if (!print_oneline)
 				printed += fprintf(fp, "\n");
 				printed += fprintf(fp, "\n");
 
 
-			callchain_cursor_advance(cursor);
 			first = false;
 			first = false;
+next:
+			callchain_cursor_advance(cursor);
 		}
 		}
 	}
 	}
 
 

+ 2 - 0
tools/perf/util/probe-event.h

@@ -18,6 +18,8 @@ struct probe_conf {
 extern struct probe_conf probe_conf;
 extern struct probe_conf probe_conf;
 extern bool probe_event_dry_run;
 extern bool probe_event_dry_run;
 
 
+struct symbol;
+
 /* kprobe-tracer and uprobe-tracer tracing point */
 /* kprobe-tracer and uprobe-tracer tracing point */
 struct probe_trace_point {
 struct probe_trace_point {
 	char		*realname;	/* function real name (if needed) */
 	char		*realname;	/* function real name (if needed) */

+ 1 - 1
tools/perf/util/symbol.c

@@ -1962,7 +1962,7 @@ static bool symbol__read_kptr_restrict(void)
 		char line[8];
 		char line[8];
 
 
 		if (fgets(line, sizeof(line), fp) != NULL)
 		if (fgets(line, sizeof(line), fp) != NULL)
-			value = (geteuid() != 0) ?
+			value = ((geteuid() != 0) || (getuid() != 0)) ?
 					(atoi(line) != 0) :
 					(atoi(line) != 0) :
 					(atoi(line) == 2);
 					(atoi(line) == 2);
 
 

+ 1 - 0
tools/perf/util/symbol.h

@@ -58,6 +58,7 @@ struct symbol {
 	u16		namelen;
 	u16		namelen;
 	u8		binding;
 	u8		binding;
 	u8		idle:1;
 	u8		idle:1;
+	u8		ignore:1;
 	u8		arch_sym;
 	u8		arch_sym;
 	char		name[0];
 	char		name[0];
 };
 };

+ 53 - 2
tools/perf/util/util.c

@@ -637,12 +637,63 @@ bool find_process(const char *name)
 	return ret ? false : true;
 	return ret ? false : true;
 }
 }
 
 
+static int
+fetch_ubuntu_kernel_version(unsigned int *puint)
+{
+	ssize_t len;
+	size_t line_len = 0;
+	char *ptr, *line = NULL;
+	int version, patchlevel, sublevel, err;
+	FILE *vsig = fopen("/proc/version_signature", "r");
+
+	if (!vsig) {
+		pr_debug("Open /proc/version_signature failed: %s\n",
+			 strerror(errno));
+		return -1;
+	}
+
+	len = getline(&line, &line_len, vsig);
+	fclose(vsig);
+	err = -1;
+	if (len <= 0) {
+		pr_debug("Reading from /proc/version_signature failed: %s\n",
+			 strerror(errno));
+		goto errout;
+	}
+
+	ptr = strrchr(line, ' ');
+	if (!ptr) {
+		pr_debug("Parsing /proc/version_signature failed: %s\n", line);
+		goto errout;
+	}
+
+	err = sscanf(ptr + 1, "%d.%d.%d",
+		     &version, &patchlevel, &sublevel);
+	if (err != 3) {
+		pr_debug("Unable to get kernel version from /proc/version_signature '%s'\n",
+			 line);
+		goto errout;
+	}
+
+	if (puint)
+		*puint = (version << 16) + (patchlevel << 8) + sublevel;
+	err = 0;
+errout:
+	free(line);
+	return err;
+}
+
 int
 int
 fetch_kernel_version(unsigned int *puint, char *str,
 fetch_kernel_version(unsigned int *puint, char *str,
 		     size_t str_size)
 		     size_t str_size)
 {
 {
 	struct utsname utsname;
 	struct utsname utsname;
 	int version, patchlevel, sublevel, err;
 	int version, patchlevel, sublevel, err;
+	bool int_ver_ready = false;
+
+	if (access("/proc/version_signature", R_OK) == 0)
+		if (!fetch_ubuntu_kernel_version(puint))
+			int_ver_ready = true;
 
 
 	if (uname(&utsname))
 	if (uname(&utsname))
 		return -1;
 		return -1;
@@ -656,12 +707,12 @@ fetch_kernel_version(unsigned int *puint, char *str,
 		     &version, &patchlevel, &sublevel);
 		     &version, &patchlevel, &sublevel);
 
 
 	if (err != 3) {
 	if (err != 3) {
-		pr_debug("Unablt to get kernel version from uname '%s'\n",
+		pr_debug("Unable to get kernel version from uname '%s'\n",
 			 utsname.release);
 			 utsname.release);
 		return -1;
 		return -1;
 	}
 	}
 
 
-	if (puint)
+	if (puint && !int_ver_ready)
 		*puint = (version << 16) + (patchlevel << 8) + sublevel;
 		*puint = (version << 16) + (patchlevel << 8) + sublevel;
 	return 0;
 	return 0;
 }
 }