mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-15 15:04:27 +08:00
perf/core improvements and fixes:
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: - 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> -----BEGIN PGP SIGNATURE----- Version: GnuPG v2 iQIcBAABCAAGBQJYOFHxAAoJENZQFvNTUqpAUXIQAIM1QJdA7zBvGpAoZPZdzOQM avQZjYN+QnW8aCAEClVph+JM7apXlp2h5u+gFmoOgzSdQlljaUqkJ2DDK26XbaTR Qo1qu/FBGc/QHLlh+5kUVly+yoSxb6N9aXa+pUZzakbt4uiPkBBz+BoPaBK0c5xt flLhP4x7x33AK6UWc4XlTgSZoYUEHTvrDKI3yCKAMcFs5YoNDwDFeFSp7zUXrhKT 5jjIUsW4ZAM7yACXhQsfDDb7OaVPFm7KHk0DwAG2H0p7oESRCXOU/09ZGFW9g3eo zQWngNB72Ed721MuqyzRfjjGuzFpNCbCQiTcB+S3Fkoqcbr9tpohZ45FM8ntBgUK jY62V8XHDHPB4jqM15uxa7NIjXsXCgUBKj9ukpbHdesblW+Cs+DyU7BAETKSrGDX gjcBK0gXlV5+AYvTGlo7abK8pZAJA2nzORFKZg4TDO2yjRmYGqAcl9cTjQOsAgcB QGiEK3KxLEfjWwcL+ebgscJUvTXbhazGX0ijS/HgE5VTc8wzaMDQB0bsBp51OZ6h tBpujHg+5zqrORHSLy48Klu1k2MrHKlGP5gQrO3NwzDA1xcVZm5fYJWEdMnckalk 2rcbJgxHGevPXjNAX8kYV/Api3yiYnijpTL46PB63WW4zZydaYuu+824sE5ME77B bafFeJqf34716vIFom6d =46x8 -----END PGP SIGNATURE----- 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>
This commit is contained in:
commit
2471cece40
@ -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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
pr_debug("maps in %s: %zd bytes\n", obj->path, size);
|
||||
/*
|
||||
* 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)).
|
||||
*/
|
||||
/*
|
||||
* 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;
|
||||
|
@ -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
tools/perf/arch/powerpc/annotate/instructions.c
Normal file
58
tools/perf/arch/powerpc/annotate/instructions.c
Normal file
@ -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;
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 @@ show_help:
|
||||
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))) {
|
||||
|
@ -28,14 +28,20 @@ const char *disassembler_style;
|
||||
const char *objdump_path;
|
||||
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 {
|
||||
const char *name;
|
||||
struct ins *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 initialized;
|
||||
void *priv;
|
||||
int (*init)(struct arch *arch);
|
||||
struct {
|
||||
char comment_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 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/x86/annotate/instructions.c"
|
||||
#include "arch/powerpc/annotate/instructions.c"
|
||||
|
||||
static struct arch architectures[] = {
|
||||
{
|
||||
.name = "arm",
|
||||
.instructions = arm__instructions,
|
||||
.nr_instructions = ARRAY_SIZE(arm__instructions),
|
||||
.objdump = {
|
||||
.comment_char = ';',
|
||||
.skip_functions_char = '+',
|
||||
},
|
||||
.init = arm__annotate_init,
|
||||
},
|
||||
{
|
||||
.name = "x86",
|
||||
@ -71,6 +121,10 @@ static struct arch architectures[] = {
|
||||
.comment_char = '#',
|
||||
},
|
||||
},
|
||||
{
|
||||
.name = "powerpc",
|
||||
.init = powerpc__annotate_init,
|
||||
},
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
char *name;
|
||||
|
||||
ops->locked.ops = zalloc(sizeof(*ops->locked.ops));
|
||||
if (ops->locked.ops == NULL)
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
return 0;
|
||||
@ -252,19 +300,19 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size,
|
||||
{
|
||||
int printed;
|
||||
|
||||
if (ops->locked.ins == NULL)
|
||||
if (ops->locked.ins.ops == NULL)
|
||||
return ins__raw_scnprintf(ins, bf, size, ops);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
else
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
if (!arch->sorted_instructions) {
|
||||
@ -434,7 +483,18 @@ static struct ins *ins__find(struct arch *arch, const char *name)
|
||||
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)
|
||||
@ -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)
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
|
||||
@ -736,7 +793,8 @@ static int disasm_line__parse(char *line, char **namep, char **rawp)
|
||||
return 0;
|
||||
|
||||
out_free_name:
|
||||
zfree(namep);
|
||||
free((void *)namep);
|
||||
*namep = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
@ -755,7 +813,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line,
|
||||
goto out_delete;
|
||||
|
||||
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;
|
||||
|
||||
disasm_line__init_ins(dl, arch, map);
|
||||
@ -774,20 +832,21 @@ out_delete:
|
||||
void disasm_line__free(struct disasm_line *dl)
|
||||
{
|
||||
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
|
||||
ins__delete(&dl->ops);
|
||||
free((void *)dl->ins.name);
|
||||
dl->ins.name = NULL;
|
||||
free(dl);
|
||||
}
|
||||
|
||||
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)
|
||||
@ -1143,7 +1202,7 @@ static int symbol__parse_objdump_line(struct symbol *sym, struct map *map,
|
||||
map__rip_2objdump(map, sym->start);
|
||||
|
||||
/* 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 = {
|
||||
.map = map,
|
||||
.addr = dl->ops.target.addr,
|
||||
@ -1173,8 +1232,8 @@ static void delete_last_nop(struct symbol *sym)
|
||||
while (!list_empty(list)) {
|
||||
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;
|
||||
} else {
|
||||
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)
|
||||
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__,
|
||||
symfs_filename, sym->name, map->unmap_ip(map, sym->start),
|
||||
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)
|
||||
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') {
|
||||
printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ",
|
||||
|
@ -11,7 +11,12 @@
|
||||
#include <linux/rbtree.h>
|
||||
#include <pthread.h>
|
||||
|
||||
struct ins;
|
||||
struct ins_ops;
|
||||
|
||||
struct ins {
|
||||
const char *name;
|
||||
struct ins_ops *ops;
|
||||
};
|
||||
|
||||
struct ins_operands {
|
||||
char *raw;
|
||||
@ -28,7 +33,7 @@ struct ins_operands {
|
||||
u64 addr;
|
||||
} source;
|
||||
struct {
|
||||
struct ins *ins;
|
||||
struct ins ins;
|
||||
struct ins_operands *ops;
|
||||
} locked;
|
||||
};
|
||||
@ -43,11 +48,6 @@ struct ins_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_call(const struct ins *ins);
|
||||
bool ins__is_ret(const struct ins *ins);
|
||||
@ -59,8 +59,7 @@ struct disasm_line {
|
||||
struct list_head node;
|
||||
s64 offset;
|
||||
char *line;
|
||||
char *name;
|
||||
struct ins *ins;
|
||||
struct ins ins;
|
||||
int line_nr;
|
||||
float ipc;
|
||||
u64 cycles;
|
||||
|
@ -392,6 +392,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel,
|
||||
#define EVSEL__PRINT_SRCLINE (1<<5)
|
||||
#define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6)
|
||||
#define EVSEL__PRINT_CALLCHAIN_ARROW (1<<7)
|
||||
#define EVSEL__PRINT_SKIP_IGNORED (1<<8)
|
||||
|
||||
struct callchain_cursor;
|
||||
|
||||
|
@ -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_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR;
|
||||
int print_arrow = print_opts & EVSEL__PRINT_CALLCHAIN_ARROW;
|
||||
int print_skip_ignored = print_opts & EVSEL__PRINT_SKIP_IGNORED;
|
||||
char s = print_oneline ? ' ' : '\t';
|
||||
bool first = true;
|
||||
|
||||
@ -124,6 +125,9 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
|
||||
if (!node)
|
||||
break;
|
||||
|
||||
if (node->sym && node->sym->ignore && print_skip_ignored)
|
||||
goto next;
|
||||
|
||||
printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " ");
|
||||
|
||||
if (print_arrow && !first)
|
||||
@ -162,8 +166,9 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment,
|
||||
if (!print_oneline)
|
||||
printed += fprintf(fp, "\n");
|
||||
|
||||
callchain_cursor_advance(cursor);
|
||||
first = false;
|
||||
next:
|
||||
callchain_cursor_advance(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,8 @@ struct probe_conf {
|
||||
extern struct probe_conf probe_conf;
|
||||
extern bool probe_event_dry_run;
|
||||
|
||||
struct symbol;
|
||||
|
||||
/* kprobe-tracer and uprobe-tracer tracing point */
|
||||
struct probe_trace_point {
|
||||
char *realname; /* function real name (if needed) */
|
||||
|
@ -1962,7 +1962,7 @@ static bool symbol__read_kptr_restrict(void)
|
||||
char line[8];
|
||||
|
||||
if (fgets(line, sizeof(line), fp) != NULL)
|
||||
value = (geteuid() != 0) ?
|
||||
value = ((geteuid() != 0) || (getuid() != 0)) ?
|
||||
(atoi(line) != 0) :
|
||||
(atoi(line) == 2);
|
||||
|
||||
|
@ -58,6 +58,7 @@ struct symbol {
|
||||
u16 namelen;
|
||||
u8 binding;
|
||||
u8 idle:1;
|
||||
u8 ignore:1;
|
||||
u8 arch_sym;
|
||||
char name[0];
|
||||
};
|
||||
|
@ -637,12 +637,63 @@ bool find_process(const char *name)
|
||||
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
|
||||
fetch_kernel_version(unsigned int *puint, char *str,
|
||||
size_t str_size)
|
||||
{
|
||||
struct utsname utsname;
|
||||
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))
|
||||
return -1;
|
||||
@ -656,12 +707,12 @@ fetch_kernel_version(unsigned int *puint, char *str,
|
||||
&version, &patchlevel, &sublevel);
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (puint)
|
||||
if (puint && !int_ver_ready)
|
||||
*puint = (version << 16) + (patchlevel << 8) + sublevel;
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user