diff options
author | Ingo Molnar <mingo@kernel.org> | 2016-11-25 12:12:41 -0500 |
---|---|---|
committer | Ingo Molnar <mingo@kernel.org> | 2016-11-25 12:12:41 -0500 |
commit | 2471cece40d61b0035360338569d338f9dea6099 (patch) | |
tree | 809f1d14defbc82fc6a957597b8b0bcb6600faed /tools | |
parent | 47414424c53a70eceb0fc6e0a35a31a2b763d5b2 (diff) | |
parent | 4708bbda5cb2f6cdc331744597527143f46394d5 (diff) |
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>
Diffstat (limited to 'tools')
-rw-r--r-- | tools/lib/bpf/libbpf.c | 142 | ||||
-rw-r--r-- | tools/perf/arch/arm/annotate/instructions.c | 147 | ||||
-rw-r--r-- | tools/perf/arch/powerpc/annotate/instructions.c | 58 | ||||
-rw-r--r-- | tools/perf/builtin-sched.c | 26 | ||||
-rw-r--r-- | tools/perf/ui/browsers/annotate.c | 18 | ||||
-rw-r--r-- | tools/perf/util/annotate.c | 157 | ||||
-rw-r--r-- | tools/perf/util/annotate.h | 17 | ||||
-rw-r--r-- | tools/perf/util/evsel.h | 1 | ||||
-rw-r--r-- | tools/perf/util/evsel_fprintf.c | 7 | ||||
-rw-r--r-- | tools/perf/util/probe-event.h | 2 | ||||
-rw-r--r-- | tools/perf/util/symbol.c | 2 | ||||
-rw-r--r-- | tools/perf/util/symbol.h | 1 | ||||
-rw-r--r-- | tools/perf/util/util.c | 55 |
13 files changed, 431 insertions, 202 deletions
diff --git a/tools/lib/bpf/libbpf.c b/tools/lib/bpf/libbpf.c index b699aea9a025..96a2b2ff1212 100644 --- a/tools/lib/bpf/libbpf.c +++ b/tools/lib/bpf/libbpf.c | |||
@@ -185,6 +185,7 @@ struct bpf_program { | |||
185 | struct bpf_map { | 185 | struct bpf_map { |
186 | int fd; | 186 | int fd; |
187 | char *name; | 187 | char *name; |
188 | size_t offset; | ||
188 | struct bpf_map_def def; | 189 | struct bpf_map_def def; |
189 | void *priv; | 190 | void *priv; |
190 | bpf_map_clear_priv_t clear_priv; | 191 | bpf_map_clear_priv_t clear_priv; |
@@ -513,57 +514,106 @@ bpf_object__init_kversion(struct bpf_object *obj, | |||
513 | } | 514 | } |
514 | 515 | ||
515 | static int | 516 | static int |
516 | bpf_object__init_maps(struct bpf_object *obj, void *data, | 517 | bpf_object__validate_maps(struct bpf_object *obj) |
517 | size_t size) | ||
518 | { | 518 | { |
519 | size_t nr_maps; | ||
520 | int i; | 519 | int i; |
521 | 520 | ||
522 | nr_maps = size / sizeof(struct bpf_map_def); | 521 | /* |
523 | if (!data || !nr_maps) { | 522 | * If there's only 1 map, the only error case should have been |
524 | pr_debug("%s doesn't need map definition\n", | 523 | * catched in bpf_object__init_maps(). |
525 | obj->path); | 524 | */ |
525 | if (!obj->maps || !obj->nr_maps || (obj->nr_maps == 1)) | ||
526 | return 0; | 526 | return 0; |
527 | } | ||
528 | 527 | ||
529 | pr_debug("maps in %s: %zd bytes\n", obj->path, size); | 528 | for (i = 1; i < obj->nr_maps; i++) { |
529 | const struct bpf_map *a = &obj->maps[i - 1]; | ||
530 | const struct bpf_map *b = &obj->maps[i]; | ||
530 | 531 | ||
531 | obj->maps = calloc(nr_maps, sizeof(obj->maps[0])); | 532 | if (b->offset - a->offset < sizeof(struct bpf_map_def)) { |
532 | if (!obj->maps) { | 533 | pr_warning("corrupted map section in %s: map \"%s\" too small\n", |
533 | pr_warning("alloc maps for object failed\n"); | 534 | obj->path, a->name); |
534 | return -ENOMEM; | 535 | return -EINVAL; |
536 | } | ||
535 | } | 537 | } |
536 | obj->nr_maps = nr_maps; | 538 | return 0; |
537 | 539 | } | |
538 | for (i = 0; i < nr_maps; i++) { | ||
539 | struct bpf_map_def *def = &obj->maps[i].def; | ||
540 | 540 | ||
541 | /* | 541 | static int compare_bpf_map(const void *_a, const void *_b) |
542 | * fill all fd with -1 so won't close incorrect | 542 | { |
543 | * fd (fd=0 is stdin) when failure (zclose won't close | 543 | const struct bpf_map *a = _a; |
544 | * negative fd)). | 544 | const struct bpf_map *b = _b; |
545 | */ | ||
546 | obj->maps[i].fd = -1; | ||
547 | 545 | ||
548 | /* Save map definition into obj->maps */ | 546 | return a->offset - b->offset; |
549 | *def = ((struct bpf_map_def *)data)[i]; | ||
550 | } | ||
551 | return 0; | ||
552 | } | 547 | } |
553 | 548 | ||
554 | static int | 549 | static int |
555 | bpf_object__init_maps_name(struct bpf_object *obj) | 550 | bpf_object__init_maps(struct bpf_object *obj) |
556 | { | 551 | { |
557 | int i; | 552 | int i, map_idx, nr_maps = 0; |
553 | Elf_Scn *scn; | ||
554 | Elf_Data *data; | ||
558 | Elf_Data *symbols = obj->efile.symbols; | 555 | Elf_Data *symbols = obj->efile.symbols; |
559 | 556 | ||
560 | if (!symbols || obj->efile.maps_shndx < 0) | 557 | if (obj->efile.maps_shndx < 0) |
558 | return -EINVAL; | ||
559 | if (!symbols) | ||
560 | return -EINVAL; | ||
561 | |||
562 | scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx); | ||
563 | if (scn) | ||
564 | data = elf_getdata(scn, NULL); | ||
565 | if (!scn || !data) { | ||
566 | pr_warning("failed to get Elf_Data from map section %d\n", | ||
567 | obj->efile.maps_shndx); | ||
561 | return -EINVAL; | 568 | return -EINVAL; |
569 | } | ||
562 | 570 | ||
571 | /* | ||
572 | * Count number of maps. Each map has a name. | ||
573 | * Array of maps is not supported: only the first element is | ||
574 | * considered. | ||
575 | * | ||
576 | * TODO: Detect array of map and report error. | ||
577 | */ | ||
563 | for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { | 578 | for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { |
564 | GElf_Sym sym; | 579 | GElf_Sym sym; |
565 | size_t map_idx; | 580 | |
581 | if (!gelf_getsym(symbols, i, &sym)) | ||
582 | continue; | ||
583 | if (sym.st_shndx != obj->efile.maps_shndx) | ||
584 | continue; | ||
585 | nr_maps++; | ||
586 | } | ||
587 | |||
588 | /* Alloc obj->maps and fill nr_maps. */ | ||
589 | pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path, | ||
590 | nr_maps, data->d_size); | ||
591 | |||
592 | if (!nr_maps) | ||
593 | return 0; | ||
594 | |||
595 | obj->maps = calloc(nr_maps, sizeof(obj->maps[0])); | ||
596 | if (!obj->maps) { | ||
597 | pr_warning("alloc maps for object failed\n"); | ||
598 | return -ENOMEM; | ||
599 | } | ||
600 | obj->nr_maps = nr_maps; | ||
601 | |||
602 | /* | ||
603 | * fill all fd with -1 so won't close incorrect | ||
604 | * fd (fd=0 is stdin) when failure (zclose won't close | ||
605 | * negative fd)). | ||
606 | */ | ||
607 | for (i = 0; i < nr_maps; i++) | ||
608 | obj->maps[i].fd = -1; | ||
609 | |||
610 | /* | ||
611 | * Fill obj->maps using data in "maps" section. | ||
612 | */ | ||
613 | for (i = 0, map_idx = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) { | ||
614 | GElf_Sym sym; | ||
566 | const char *map_name; | 615 | const char *map_name; |
616 | struct bpf_map_def *def; | ||
567 | 617 | ||
568 | if (!gelf_getsym(symbols, i, &sym)) | 618 | if (!gelf_getsym(symbols, i, &sym)) |
569 | continue; | 619 | continue; |
@@ -573,21 +623,27 @@ bpf_object__init_maps_name(struct bpf_object *obj) | |||
573 | map_name = elf_strptr(obj->efile.elf, | 623 | map_name = elf_strptr(obj->efile.elf, |
574 | obj->efile.strtabidx, | 624 | obj->efile.strtabidx, |
575 | sym.st_name); | 625 | sym.st_name); |
576 | map_idx = sym.st_value / sizeof(struct bpf_map_def); | 626 | obj->maps[map_idx].offset = sym.st_value; |
577 | if (map_idx >= obj->nr_maps) { | 627 | if (sym.st_value + sizeof(struct bpf_map_def) > data->d_size) { |
578 | pr_warning("index of map \"%s\" is buggy: %zu > %zu\n", | 628 | pr_warning("corrupted maps section in %s: last map \"%s\" too small\n", |
579 | map_name, map_idx, obj->nr_maps); | 629 | obj->path, map_name); |
580 | continue; | 630 | return -EINVAL; |
581 | } | 631 | } |
632 | |||
582 | obj->maps[map_idx].name = strdup(map_name); | 633 | obj->maps[map_idx].name = strdup(map_name); |
583 | if (!obj->maps[map_idx].name) { | 634 | if (!obj->maps[map_idx].name) { |
584 | pr_warning("failed to alloc map name\n"); | 635 | pr_warning("failed to alloc map name\n"); |
585 | return -ENOMEM; | 636 | return -ENOMEM; |
586 | } | 637 | } |
587 | pr_debug("map %zu is \"%s\"\n", map_idx, | 638 | pr_debug("map %d is \"%s\"\n", map_idx, |
588 | obj->maps[map_idx].name); | 639 | obj->maps[map_idx].name); |
640 | def = (struct bpf_map_def *)(data->d_buf + sym.st_value); | ||
641 | obj->maps[map_idx].def = *def; | ||
642 | map_idx++; | ||
589 | } | 643 | } |
590 | return 0; | 644 | |
645 | qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), compare_bpf_map); | ||
646 | return bpf_object__validate_maps(obj); | ||
591 | } | 647 | } |
592 | 648 | ||
593 | static int bpf_object__elf_collect(struct bpf_object *obj) | 649 | static int bpf_object__elf_collect(struct bpf_object *obj) |
@@ -645,11 +701,9 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
645 | err = bpf_object__init_kversion(obj, | 701 | err = bpf_object__init_kversion(obj, |
646 | data->d_buf, | 702 | data->d_buf, |
647 | data->d_size); | 703 | data->d_size); |
648 | else if (strcmp(name, "maps") == 0) { | 704 | else if (strcmp(name, "maps") == 0) |
649 | err = bpf_object__init_maps(obj, data->d_buf, | ||
650 | data->d_size); | ||
651 | obj->efile.maps_shndx = idx; | 705 | obj->efile.maps_shndx = idx; |
652 | } else if (sh.sh_type == SHT_SYMTAB) { | 706 | else if (sh.sh_type == SHT_SYMTAB) { |
653 | if (obj->efile.symbols) { | 707 | if (obj->efile.symbols) { |
654 | pr_warning("bpf: multiple SYMTAB in %s\n", | 708 | pr_warning("bpf: multiple SYMTAB in %s\n", |
655 | obj->path); | 709 | obj->path); |
@@ -698,7 +752,7 @@ static int bpf_object__elf_collect(struct bpf_object *obj) | |||
698 | return LIBBPF_ERRNO__FORMAT; | 752 | return LIBBPF_ERRNO__FORMAT; |
699 | } | 753 | } |
700 | if (obj->efile.maps_shndx >= 0) | 754 | if (obj->efile.maps_shndx >= 0) |
701 | err = bpf_object__init_maps_name(obj); | 755 | err = bpf_object__init_maps(obj); |
702 | out: | 756 | out: |
703 | return err; | 757 | return err; |
704 | } | 758 | } |
@@ -807,7 +861,7 @@ bpf_object__create_maps(struct bpf_object *obj) | |||
807 | zclose(obj->maps[j].fd); | 861 | zclose(obj->maps[j].fd); |
808 | return err; | 862 | return err; |
809 | } | 863 | } |
810 | pr_debug("create map: fd=%d\n", *pfd); | 864 | pr_debug("create map %s: fd=%d\n", obj->maps[i].name, *pfd); |
811 | } | 865 | } |
812 | 866 | ||
813 | return 0; | 867 | return 0; |
diff --git a/tools/perf/arch/arm/annotate/instructions.c b/tools/perf/arch/arm/annotate/instructions.c index d67b8aa26274..1ce0872b1726 100644 --- a/tools/perf/arch/arm/annotate/instructions.c +++ b/tools/perf/arch/arm/annotate/instructions.c | |||
@@ -1,90 +1,59 @@ | |||
1 | static struct ins arm__instructions[] = { | 1 | #include <sys/types.h> |
2 | { .name = "add", .ops = &mov_ops, }, | 2 | #include <regex.h> |
3 | { .name = "addl", .ops = &mov_ops, }, | 3 | |
4 | { .name = "addq", .ops = &mov_ops, }, | 4 | struct arm_annotate { |
5 | { .name = "addw", .ops = &mov_ops, }, | 5 | regex_t call_insn, |
6 | { .name = "and", .ops = &mov_ops, }, | 6 | jump_insn; |
7 | { .name = "b", .ops = &jump_ops, }, // might also be a call | ||
8 | { .name = "bcc", .ops = &jump_ops, }, | ||
9 | { .name = "bcs", .ops = &jump_ops, }, | ||
10 | { .name = "beq", .ops = &jump_ops, }, | ||
11 | { .name = "bge", .ops = &jump_ops, }, | ||
12 | { .name = "bgt", .ops = &jump_ops, }, | ||
13 | { .name = "bhi", .ops = &jump_ops, }, | ||
14 | { .name = "bl", .ops = &call_ops, }, | ||
15 | { .name = "bls", .ops = &jump_ops, }, | ||
16 | { .name = "blt", .ops = &jump_ops, }, | ||
17 | { .name = "blx", .ops = &call_ops, }, | ||
18 | { .name = "bne", .ops = &jump_ops, }, | ||
19 | { .name = "bts", .ops = &mov_ops, }, | ||
20 | { .name = "call", .ops = &call_ops, }, | ||
21 | { .name = "callq", .ops = &call_ops, }, | ||
22 | { .name = "cmp", .ops = &mov_ops, }, | ||
23 | { .name = "cmpb", .ops = &mov_ops, }, | ||
24 | { .name = "cmpl", .ops = &mov_ops, }, | ||
25 | { .name = "cmpq", .ops = &mov_ops, }, | ||
26 | { .name = "cmpw", .ops = &mov_ops, }, | ||
27 | { .name = "cmpxch", .ops = &mov_ops, }, | ||
28 | { .name = "dec", .ops = &dec_ops, }, | ||
29 | { .name = "decl", .ops = &dec_ops, }, | ||
30 | { .name = "imul", .ops = &mov_ops, }, | ||
31 | { .name = "inc", .ops = &dec_ops, }, | ||
32 | { .name = "incl", .ops = &dec_ops, }, | ||
33 | { .name = "ja", .ops = &jump_ops, }, | ||
34 | { .name = "jae", .ops = &jump_ops, }, | ||
35 | { .name = "jb", .ops = &jump_ops, }, | ||
36 | { .name = "jbe", .ops = &jump_ops, }, | ||
37 | { .name = "jc", .ops = &jump_ops, }, | ||
38 | { .name = "jcxz", .ops = &jump_ops, }, | ||
39 | { .name = "je", .ops = &jump_ops, }, | ||
40 | { .name = "jecxz", .ops = &jump_ops, }, | ||
41 | { .name = "jg", .ops = &jump_ops, }, | ||
42 | { .name = "jge", .ops = &jump_ops, }, | ||
43 | { .name = "jl", .ops = &jump_ops, }, | ||
44 | { .name = "jle", .ops = &jump_ops, }, | ||
45 | { .name = "jmp", .ops = &jump_ops, }, | ||
46 | { .name = "jmpq", .ops = &jump_ops, }, | ||
47 | { .name = "jna", .ops = &jump_ops, }, | ||
48 | { .name = "jnae", .ops = &jump_ops, }, | ||
49 | { .name = "jnb", .ops = &jump_ops, }, | ||
50 | { .name = "jnbe", .ops = &jump_ops, }, | ||
51 | { .name = "jnc", .ops = &jump_ops, }, | ||
52 | { .name = "jne", .ops = &jump_ops, }, | ||
53 | { .name = "jng", .ops = &jump_ops, }, | ||
54 | { .name = "jnge", .ops = &jump_ops, }, | ||
55 | { .name = "jnl", .ops = &jump_ops, }, | ||
56 | { .name = "jnle", .ops = &jump_ops, }, | ||
57 | { .name = "jno", .ops = &jump_ops, }, | ||
58 | { .name = "jnp", .ops = &jump_ops, }, | ||
59 | { .name = "jns", .ops = &jump_ops, }, | ||
60 | { .name = "jnz", .ops = &jump_ops, }, | ||
61 | { .name = "jo", .ops = &jump_ops, }, | ||
62 | { .name = "jp", .ops = &jump_ops, }, | ||
63 | { .name = "jpe", .ops = &jump_ops, }, | ||
64 | { .name = "jpo", .ops = &jump_ops, }, | ||
65 | { .name = "jrcxz", .ops = &jump_ops, }, | ||
66 | { .name = "js", .ops = &jump_ops, }, | ||
67 | { .name = "jz", .ops = &jump_ops, }, | ||
68 | { .name = "lea", .ops = &mov_ops, }, | ||
69 | { .name = "lock", .ops = &lock_ops, }, | ||
70 | { .name = "mov", .ops = &mov_ops, }, | ||
71 | { .name = "movb", .ops = &mov_ops, }, | ||
72 | { .name = "movdqa", .ops = &mov_ops, }, | ||
73 | { .name = "movl", .ops = &mov_ops, }, | ||
74 | { .name = "movq", .ops = &mov_ops, }, | ||
75 | { .name = "movslq", .ops = &mov_ops, }, | ||
76 | { .name = "movzbl", .ops = &mov_ops, }, | ||
77 | { .name = "movzwl", .ops = &mov_ops, }, | ||
78 | { .name = "nop", .ops = &nop_ops, }, | ||
79 | { .name = "nopl", .ops = &nop_ops, }, | ||
80 | { .name = "nopw", .ops = &nop_ops, }, | ||
81 | { .name = "or", .ops = &mov_ops, }, | ||
82 | { .name = "orl", .ops = &mov_ops, }, | ||
83 | { .name = "test", .ops = &mov_ops, }, | ||
84 | { .name = "testb", .ops = &mov_ops, }, | ||
85 | { .name = "testl", .ops = &mov_ops, }, | ||
86 | { .name = "xadd", .ops = &mov_ops, }, | ||
87 | { .name = "xbeginl", .ops = &jump_ops, }, | ||
88 | { .name = "xbeginq", .ops = &jump_ops, }, | ||
89 | { .name = "retq", .ops = &ret_ops, }, | ||
90 | }; | 7 | }; |
8 | |||
9 | static struct ins_ops *arm__associate_instruction_ops(struct arch *arch, const char *name) | ||
10 | { | ||
11 | struct arm_annotate *arm = arch->priv; | ||
12 | struct ins_ops *ops; | ||
13 | regmatch_t match[2]; | ||
14 | |||
15 | if (!regexec(&arm->call_insn, name, 2, match, 0)) | ||
16 | ops = &call_ops; | ||
17 | else if (!regexec(&arm->jump_insn, name, 2, match, 0)) | ||
18 | ops = &jump_ops; | ||
19 | else | ||
20 | return NULL; | ||
21 | |||
22 | arch__associate_ins_ops(arch, name, ops); | ||
23 | return ops; | ||
24 | } | ||
25 | |||
26 | static int arm__annotate_init(struct arch *arch) | ||
27 | { | ||
28 | struct arm_annotate *arm; | ||
29 | int err; | ||
30 | |||
31 | if (arch->initialized) | ||
32 | return 0; | ||
33 | |||
34 | arm = zalloc(sizeof(*arm)); | ||
35 | if (!arm) | ||
36 | return -1; | ||
37 | |||
38 | #define ARM_CONDS "(cc|cs|eq|ge|gt|hi|le|ls|lt|mi|ne|pl|vc|vs)" | ||
39 | err = regcomp(&arm->call_insn, "^blx?" ARM_CONDS "?$", REG_EXTENDED); | ||
40 | if (err) | ||
41 | goto out_free_arm; | ||
42 | err = regcomp(&arm->jump_insn, "^bx?" ARM_CONDS "?$", REG_EXTENDED); | ||
43 | if (err) | ||
44 | goto out_free_call; | ||
45 | #undef ARM_CONDS | ||
46 | |||
47 | arch->initialized = true; | ||
48 | arch->priv = arm; | ||
49 | arch->associate_instruction_ops = arm__associate_instruction_ops; | ||
50 | arch->objdump.comment_char = ';'; | ||
51 | arch->objdump.skip_functions_char = '+'; | ||
52 | return 0; | ||
53 | |||
54 | out_free_call: | ||
55 | regfree(&arm->call_insn); | ||
56 | out_free_arm: | ||
57 | free(arm); | ||
58 | return -1; | ||
59 | } | ||
diff --git a/tools/perf/arch/powerpc/annotate/instructions.c b/tools/perf/arch/powerpc/annotate/instructions.c new file mode 100644 index 000000000000..3c4004db81b9 --- /dev/null +++ b/tools/perf/arch/powerpc/annotate/instructions.c | |||
@@ -0,0 +1,58 @@ | |||
1 | static struct ins_ops *powerpc__associate_instruction_ops(struct arch *arch, const char *name) | ||
2 | { | ||
3 | int i; | ||
4 | struct ins_ops *ops; | ||
5 | |||
6 | /* | ||
7 | * - Interested only if instruction starts with 'b'. | ||
8 | * - Few start with 'b', but aren't branch instructions. | ||
9 | */ | ||
10 | if (name[0] != 'b' || | ||
11 | !strncmp(name, "bcd", 3) || | ||
12 | !strncmp(name, "brinc", 5) || | ||
13 | !strncmp(name, "bper", 4)) | ||
14 | return NULL; | ||
15 | |||
16 | ops = &jump_ops; | ||
17 | |||
18 | i = strlen(name) - 1; | ||
19 | if (i < 0) | ||
20 | return NULL; | ||
21 | |||
22 | /* ignore optional hints at the end of the instructions */ | ||
23 | if (name[i] == '+' || name[i] == '-') | ||
24 | i--; | ||
25 | |||
26 | if (name[i] == 'l' || (name[i] == 'a' && name[i-1] == 'l')) { | ||
27 | /* | ||
28 | * if the instruction ends up with 'l' or 'la', then | ||
29 | * those are considered 'calls' since they update LR. | ||
30 | * ... except for 'bnl' which is branch if not less than | ||
31 | * and the absolute form of the same. | ||
32 | */ | ||
33 | if (strcmp(name, "bnl") && strcmp(name, "bnl+") && | ||
34 | strcmp(name, "bnl-") && strcmp(name, "bnla") && | ||
35 | strcmp(name, "bnla+") && strcmp(name, "bnla-")) | ||
36 | ops = &call_ops; | ||
37 | } | ||
38 | if (name[i] == 'r' && name[i-1] == 'l') | ||
39 | /* | ||
40 | * instructions ending with 'lr' are considered to be | ||
41 | * return instructions | ||
42 | */ | ||
43 | ops = &ret_ops; | ||
44 | |||
45 | arch__associate_ins_ops(arch, name, ops); | ||
46 | return ops; | ||
47 | } | ||
48 | |||
49 | static int powerpc__annotate_init(struct arch *arch) | ||
50 | { | ||
51 | if (!arch->initialized) { | ||
52 | arch->initialized = true; | ||
53 | arch->associate_instruction_ops = powerpc__associate_instruction_ops; | ||
54 | arch->objdump.comment_char = '#'; | ||
55 | } | ||
56 | |||
57 | return 0; | ||
58 | } | ||
diff --git a/tools/perf/builtin-sched.c b/tools/perf/builtin-sched.c index 829468defa07..a49a032f5b15 100644 --- a/tools/perf/builtin-sched.c +++ b/tools/perf/builtin-sched.c | |||
@@ -1876,7 +1876,8 @@ static void timehist_print_sample(struct perf_sched *sched, | |||
1876 | 1876 | ||
1877 | sample__fprintf_sym(sample, al, 0, | 1877 | sample__fprintf_sym(sample, al, 0, |
1878 | EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE | | 1878 | EVSEL__PRINT_SYM | EVSEL__PRINT_ONELINE | |
1879 | EVSEL__PRINT_CALLCHAIN_ARROW, | 1879 | EVSEL__PRINT_CALLCHAIN_ARROW | |
1880 | EVSEL__PRINT_SKIP_IGNORED, | ||
1880 | &callchain_cursor, stdout); | 1881 | &callchain_cursor, stdout); |
1881 | 1882 | ||
1882 | out: | 1883 | out: |
@@ -1959,13 +1960,34 @@ static bool is_idle_sample(struct perf_sched *sched, | |||
1959 | return false; | 1960 | return false; |
1960 | 1961 | ||
1961 | if (thread__resolve_callchain(thread, cursor, evsel, sample, | 1962 | if (thread__resolve_callchain(thread, cursor, evsel, sample, |
1962 | NULL, NULL, sched->max_stack) != 0) { | 1963 | NULL, NULL, sched->max_stack + 2) != 0) { |
1963 | if (verbose) | 1964 | if (verbose) |
1964 | error("Failed to resolve callchain. Skipping\n"); | 1965 | error("Failed to resolve callchain. Skipping\n"); |
1965 | 1966 | ||
1966 | return false; | 1967 | return false; |
1967 | } | 1968 | } |
1969 | |||
1968 | callchain_cursor_commit(cursor); | 1970 | callchain_cursor_commit(cursor); |
1971 | |||
1972 | while (true) { | ||
1973 | struct callchain_cursor_node *node; | ||
1974 | struct symbol *sym; | ||
1975 | |||
1976 | node = callchain_cursor_current(cursor); | ||
1977 | if (node == NULL) | ||
1978 | break; | ||
1979 | |||
1980 | sym = node->sym; | ||
1981 | if (sym && sym->name) { | ||
1982 | if (!strcmp(sym->name, "schedule") || | ||
1983 | !strcmp(sym->name, "__schedule") || | ||
1984 | !strcmp(sym->name, "preempt_schedule")) | ||
1985 | sym->ignore = 1; | ||
1986 | } | ||
1987 | |||
1988 | callchain_cursor_advance(cursor); | ||
1989 | } | ||
1990 | |||
1969 | return false; | 1991 | return false; |
1970 | } | 1992 | } |
1971 | 1993 | ||
diff --git a/tools/perf/ui/browsers/annotate.c b/tools/perf/ui/browsers/annotate.c index e6e9f7d80dbd..cee0eee31ce6 100644 --- a/tools/perf/ui/browsers/annotate.c +++ b/tools/perf/ui/browsers/annotate.c | |||
@@ -213,17 +213,17 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
213 | ui_browser__write_nstring(browser, bf, printed); | 213 | ui_browser__write_nstring(browser, bf, printed); |
214 | if (change_color) | 214 | if (change_color) |
215 | ui_browser__set_color(browser, color); | 215 | ui_browser__set_color(browser, color); |
216 | if (dl->ins && dl->ins->ops->scnprintf) { | 216 | if (dl->ins.ops && dl->ins.ops->scnprintf) { |
217 | if (ins__is_jump(dl->ins)) { | 217 | if (ins__is_jump(&dl->ins)) { |
218 | bool fwd = dl->ops.target.offset > (u64)dl->offset; | 218 | bool fwd = dl->ops.target.offset > (u64)dl->offset; |
219 | 219 | ||
220 | ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR : | 220 | ui_browser__write_graph(browser, fwd ? SLSMG_DARROW_CHAR : |
221 | SLSMG_UARROW_CHAR); | 221 | SLSMG_UARROW_CHAR); |
222 | SLsmg_write_char(' '); | 222 | SLsmg_write_char(' '); |
223 | } else if (ins__is_call(dl->ins)) { | 223 | } else if (ins__is_call(&dl->ins)) { |
224 | ui_browser__write_graph(browser, SLSMG_RARROW_CHAR); | 224 | ui_browser__write_graph(browser, SLSMG_RARROW_CHAR); |
225 | SLsmg_write_char(' '); | 225 | SLsmg_write_char(' '); |
226 | } else if (ins__is_ret(dl->ins)) { | 226 | } else if (ins__is_ret(&dl->ins)) { |
227 | ui_browser__write_graph(browser, SLSMG_LARROW_CHAR); | 227 | ui_browser__write_graph(browser, SLSMG_LARROW_CHAR); |
228 | SLsmg_write_char(' '); | 228 | SLsmg_write_char(' '); |
229 | } else { | 229 | } else { |
@@ -243,7 +243,7 @@ static void annotate_browser__write(struct ui_browser *browser, void *entry, int | |||
243 | 243 | ||
244 | static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) | 244 | static bool disasm_line__is_valid_jump(struct disasm_line *dl, struct symbol *sym) |
245 | { | 245 | { |
246 | if (!dl || !dl->ins || !ins__is_jump(dl->ins) | 246 | if (!dl || !dl->ins.ops || !ins__is_jump(&dl->ins) |
247 | || !disasm_line__has_offset(dl) | 247 | || !disasm_line__has_offset(dl) |
248 | || dl->ops.target.offset >= symbol__size(sym)) | 248 | || dl->ops.target.offset >= symbol__size(sym)) |
249 | return false; | 249 | return false; |
@@ -492,7 +492,7 @@ static bool annotate_browser__callq(struct annotate_browser *browser, | |||
492 | }; | 492 | }; |
493 | char title[SYM_TITLE_MAX_SIZE]; | 493 | char title[SYM_TITLE_MAX_SIZE]; |
494 | 494 | ||
495 | if (!ins__is_call(dl->ins)) | 495 | if (!ins__is_call(&dl->ins)) |
496 | return false; | 496 | return false; |
497 | 497 | ||
498 | if (map_groups__find_ams(&target) || | 498 | if (map_groups__find_ams(&target) || |
@@ -545,7 +545,7 @@ static bool annotate_browser__jump(struct annotate_browser *browser) | |||
545 | struct disasm_line *dl = browser->selection; | 545 | struct disasm_line *dl = browser->selection; |
546 | s64 idx; | 546 | s64 idx; |
547 | 547 | ||
548 | if (!ins__is_jump(dl->ins)) | 548 | if (!ins__is_jump(&dl->ins)) |
549 | return false; | 549 | return false; |
550 | 550 | ||
551 | dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); | 551 | dl = annotate_browser__find_offset(browser, dl->ops.target.offset, &idx); |
@@ -841,9 +841,9 @@ show_help: | |||
841 | ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); | 841 | ui_helpline__puts("Huh? No selection. Report to linux-kernel@vger.kernel.org"); |
842 | else if (browser->selection->offset == -1) | 842 | else if (browser->selection->offset == -1) |
843 | ui_helpline__puts("Actions are only available for assembly lines."); | 843 | ui_helpline__puts("Actions are only available for assembly lines."); |
844 | else if (!browser->selection->ins) | 844 | else if (!browser->selection->ins.ops) |
845 | goto show_sup_ins; | 845 | goto show_sup_ins; |
846 | else if (ins__is_ret(browser->selection->ins)) | 846 | else if (ins__is_ret(&browser->selection->ins)) |
847 | goto out; | 847 | goto out; |
848 | else if (!(annotate_browser__jump(browser) || | 848 | else if (!(annotate_browser__jump(browser) || |
849 | annotate_browser__callq(browser, evsel, hbt))) { | 849 | annotate_browser__callq(browser, evsel, hbt))) { |
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c index 095d90a9077f..3e34ee0fde28 100644 --- a/tools/perf/util/annotate.c +++ b/tools/perf/util/annotate.c | |||
@@ -28,14 +28,20 @@ const char *disassembler_style; | |||
28 | const char *objdump_path; | 28 | const char *objdump_path; |
29 | static regex_t file_lineno; | 29 | static regex_t file_lineno; |
30 | 30 | ||
31 | static struct ins *ins__find(struct arch *arch, const char *name); | 31 | static struct ins_ops *ins__find(struct arch *arch, const char *name); |
32 | static int disasm_line__parse(char *line, char **namep, char **rawp); | 32 | static void ins__sort(struct arch *arch); |
33 | static int disasm_line__parse(char *line, const char **namep, char **rawp); | ||
33 | 34 | ||
34 | struct arch { | 35 | struct arch { |
35 | const char *name; | 36 | const char *name; |
36 | struct ins *instructions; | 37 | struct ins *instructions; |
37 | size_t nr_instructions; | 38 | size_t nr_instructions; |
39 | size_t nr_instructions_allocated; | ||
40 | struct ins_ops *(*associate_instruction_ops)(struct arch *arch, const char *name); | ||
38 | bool sorted_instructions; | 41 | bool sorted_instructions; |
42 | bool initialized; | ||
43 | void *priv; | ||
44 | int (*init)(struct arch *arch); | ||
39 | struct { | 45 | struct { |
40 | char comment_char; | 46 | char comment_char; |
41 | char skip_functions_char; | 47 | char skip_functions_char; |
@@ -50,18 +56,62 @@ static struct ins_ops nop_ops; | |||
50 | static struct ins_ops lock_ops; | 56 | static struct ins_ops lock_ops; |
51 | static struct ins_ops ret_ops; | 57 | static struct ins_ops ret_ops; |
52 | 58 | ||
59 | static int arch__grow_instructions(struct arch *arch) | ||
60 | { | ||
61 | struct ins *new_instructions; | ||
62 | size_t new_nr_allocated; | ||
63 | |||
64 | if (arch->nr_instructions_allocated == 0 && arch->instructions) | ||
65 | goto grow_from_non_allocated_table; | ||
66 | |||
67 | new_nr_allocated = arch->nr_instructions_allocated + 128; | ||
68 | new_instructions = realloc(arch->instructions, new_nr_allocated * sizeof(struct ins)); | ||
69 | if (new_instructions == NULL) | ||
70 | return -1; | ||
71 | |||
72 | out_update_instructions: | ||
73 | arch->instructions = new_instructions; | ||
74 | arch->nr_instructions_allocated = new_nr_allocated; | ||
75 | return 0; | ||
76 | |||
77 | grow_from_non_allocated_table: | ||
78 | new_nr_allocated = arch->nr_instructions + 128; | ||
79 | new_instructions = calloc(new_nr_allocated, sizeof(struct ins)); | ||
80 | if (new_instructions == NULL) | ||
81 | return -1; | ||
82 | |||
83 | memcpy(new_instructions, arch->instructions, arch->nr_instructions); | ||
84 | goto out_update_instructions; | ||
85 | } | ||
86 | |||
87 | static int arch__associate_ins_ops(struct arch* arch, const char *name, struct ins_ops *ops) | ||
88 | { | ||
89 | struct ins *ins; | ||
90 | |||
91 | if (arch->nr_instructions == arch->nr_instructions_allocated && | ||
92 | arch__grow_instructions(arch)) | ||
93 | return -1; | ||
94 | |||
95 | ins = &arch->instructions[arch->nr_instructions]; | ||
96 | ins->name = strdup(name); | ||
97 | if (!ins->name) | ||
98 | return -1; | ||
99 | |||
100 | ins->ops = ops; | ||
101 | arch->nr_instructions++; | ||
102 | |||
103 | ins__sort(arch); | ||
104 | return 0; | ||
105 | } | ||
106 | |||
53 | #include "arch/arm/annotate/instructions.c" | 107 | #include "arch/arm/annotate/instructions.c" |
54 | #include "arch/x86/annotate/instructions.c" | 108 | #include "arch/x86/annotate/instructions.c" |
109 | #include "arch/powerpc/annotate/instructions.c" | ||
55 | 110 | ||
56 | static struct arch architectures[] = { | 111 | static struct arch architectures[] = { |
57 | { | 112 | { |
58 | .name = "arm", | 113 | .name = "arm", |
59 | .instructions = arm__instructions, | 114 | .init = arm__annotate_init, |
60 | .nr_instructions = ARRAY_SIZE(arm__instructions), | ||
61 | .objdump = { | ||
62 | .comment_char = ';', | ||
63 | .skip_functions_char = '+', | ||
64 | }, | ||
65 | }, | 115 | }, |
66 | { | 116 | { |
67 | .name = "x86", | 117 | .name = "x86", |
@@ -71,6 +121,10 @@ static struct arch architectures[] = { | |||
71 | .comment_char = '#', | 121 | .comment_char = '#', |
72 | }, | 122 | }, |
73 | }, | 123 | }, |
124 | { | ||
125 | .name = "powerpc", | ||
126 | .init = powerpc__annotate_init, | ||
127 | }, | ||
74 | }; | 128 | }; |
75 | 129 | ||
76 | static void ins__delete(struct ins_operands *ops) | 130 | static void ins__delete(struct ins_operands *ops) |
@@ -218,26 +272,20 @@ static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep) | |||
218 | 272 | ||
219 | static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map) | 273 | static int lock__parse(struct arch *arch, struct ins_operands *ops, struct map *map) |
220 | { | 274 | { |
221 | char *name; | ||
222 | |||
223 | ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); | 275 | ops->locked.ops = zalloc(sizeof(*ops->locked.ops)); |
224 | if (ops->locked.ops == NULL) | 276 | if (ops->locked.ops == NULL) |
225 | return 0; | 277 | return 0; |
226 | 278 | ||
227 | if (disasm_line__parse(ops->raw, &name, &ops->locked.ops->raw) < 0) | 279 | if (disasm_line__parse(ops->raw, &ops->locked.ins.name, &ops->locked.ops->raw) < 0) |
228 | goto out_free_ops; | 280 | goto out_free_ops; |
229 | 281 | ||
230 | ops->locked.ins = ins__find(arch, name); | 282 | ops->locked.ins.ops = ins__find(arch, ops->locked.ins.name); |
231 | free(name); | ||
232 | 283 | ||
233 | if (ops->locked.ins == NULL) | 284 | if (ops->locked.ins.ops == NULL) |
234 | goto out_free_ops; | 285 | goto out_free_ops; |
235 | 286 | ||
236 | if (!ops->locked.ins->ops) | 287 | if (ops->locked.ins.ops->parse && |
237 | return 0; | 288 | ops->locked.ins.ops->parse(arch, ops->locked.ops, map) < 0) |
238 | |||
239 | if (ops->locked.ins->ops->parse && | ||
240 | ops->locked.ins->ops->parse(arch, ops->locked.ops, map) < 0) | ||
241 | goto out_free_ops; | 289 | goto out_free_ops; |
242 | 290 | ||
243 | return 0; | 291 | return 0; |
@@ -252,19 +300,19 @@ static int lock__scnprintf(struct ins *ins, char *bf, size_t size, | |||
252 | { | 300 | { |
253 | int printed; | 301 | int printed; |
254 | 302 | ||
255 | if (ops->locked.ins == NULL) | 303 | if (ops->locked.ins.ops == NULL) |
256 | return ins__raw_scnprintf(ins, bf, size, ops); | 304 | return ins__raw_scnprintf(ins, bf, size, ops); |
257 | 305 | ||
258 | printed = scnprintf(bf, size, "%-6.6s ", ins->name); | 306 | printed = scnprintf(bf, size, "%-6.6s ", ins->name); |
259 | return printed + ins__scnprintf(ops->locked.ins, bf + printed, | 307 | return printed + ins__scnprintf(&ops->locked.ins, bf + printed, |
260 | size - printed, ops->locked.ops); | 308 | size - printed, ops->locked.ops); |
261 | } | 309 | } |
262 | 310 | ||
263 | static void lock__delete(struct ins_operands *ops) | 311 | static void lock__delete(struct ins_operands *ops) |
264 | { | 312 | { |
265 | struct ins *ins = ops->locked.ins; | 313 | struct ins *ins = &ops->locked.ins; |
266 | 314 | ||
267 | if (ins && ins->ops->free) | 315 | if (ins->ops && ins->ops->free) |
268 | ins->ops->free(ops->locked.ops); | 316 | ins->ops->free(ops->locked.ops); |
269 | else | 317 | else |
270 | ins__delete(ops->locked.ops); | 318 | ins__delete(ops->locked.ops); |
@@ -425,8 +473,9 @@ static void ins__sort(struct arch *arch) | |||
425 | qsort(arch->instructions, nmemb, sizeof(struct ins), ins__cmp); | 473 | qsort(arch->instructions, nmemb, sizeof(struct ins), ins__cmp); |
426 | } | 474 | } |
427 | 475 | ||
428 | static struct ins *ins__find(struct arch *arch, const char *name) | 476 | static struct ins_ops *__ins__find(struct arch *arch, const char *name) |
429 | { | 477 | { |
478 | struct ins *ins; | ||
430 | const int nmemb = arch->nr_instructions; | 479 | const int nmemb = arch->nr_instructions; |
431 | 480 | ||
432 | if (!arch->sorted_instructions) { | 481 | if (!arch->sorted_instructions) { |
@@ -434,7 +483,18 @@ static struct ins *ins__find(struct arch *arch, const char *name) | |||
434 | arch->sorted_instructions = true; | 483 | arch->sorted_instructions = true; |
435 | } | 484 | } |
436 | 485 | ||
437 | return bsearch(name, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp); | 486 | ins = bsearch(name, arch->instructions, nmemb, sizeof(struct ins), ins__key_cmp); |
487 | return ins ? ins->ops : NULL; | ||
488 | } | ||
489 | |||
490 | static struct ins_ops *ins__find(struct arch *arch, const char *name) | ||
491 | { | ||
492 | struct ins_ops *ops = __ins__find(arch, name); | ||
493 | |||
494 | if (!ops && arch->associate_instruction_ops) | ||
495 | ops = arch->associate_instruction_ops(arch, name); | ||
496 | |||
497 | return ops; | ||
438 | } | 498 | } |
439 | 499 | ||
440 | static int arch__key_cmp(const void *name, const void *archp) | 500 | 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) | |||
691 | 751 | ||
692 | static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map) | 752 | static void disasm_line__init_ins(struct disasm_line *dl, struct arch *arch, struct map *map) |
693 | { | 753 | { |
694 | dl->ins = ins__find(arch, dl->name); | 754 | dl->ins.ops = ins__find(arch, dl->ins.name); |
695 | 755 | ||
696 | if (dl->ins == NULL) | 756 | if (!dl->ins.ops) |
697 | return; | 757 | return; |
698 | 758 | ||
699 | if (!dl->ins->ops) | 759 | if (dl->ins.ops->parse && dl->ins.ops->parse(arch, &dl->ops, map) < 0) |
700 | return; | 760 | dl->ins.ops = NULL; |
701 | |||
702 | if (dl->ins->ops->parse && dl->ins->ops->parse(arch, &dl->ops, map) < 0) | ||
703 | dl->ins = NULL; | ||
704 | } | 761 | } |
705 | 762 | ||
706 | static int disasm_line__parse(char *line, char **namep, char **rawp) | 763 | static int disasm_line__parse(char *line, const char **namep, char **rawp) |
707 | { | 764 | { |
708 | char *name = line, tmp; | 765 | char *name = line, tmp; |
709 | 766 | ||
@@ -736,7 +793,8 @@ static int disasm_line__parse(char *line, char **namep, char **rawp) | |||
736 | return 0; | 793 | return 0; |
737 | 794 | ||
738 | out_free_name: | 795 | out_free_name: |
739 | zfree(namep); | 796 | free((void *)namep); |
797 | *namep = NULL; | ||
740 | return -1; | 798 | return -1; |
741 | } | 799 | } |
742 | 800 | ||
@@ -755,7 +813,7 @@ static struct disasm_line *disasm_line__new(s64 offset, char *line, | |||
755 | goto out_delete; | 813 | goto out_delete; |
756 | 814 | ||
757 | if (offset != -1) { | 815 | if (offset != -1) { |
758 | if (disasm_line__parse(dl->line, &dl->name, &dl->ops.raw) < 0) | 816 | if (disasm_line__parse(dl->line, &dl->ins.name, &dl->ops.raw) < 0) |
759 | goto out_free_line; | 817 | goto out_free_line; |
760 | 818 | ||
761 | disasm_line__init_ins(dl, arch, map); | 819 | disasm_line__init_ins(dl, arch, map); |
@@ -774,20 +832,21 @@ out_delete: | |||
774 | void disasm_line__free(struct disasm_line *dl) | 832 | void disasm_line__free(struct disasm_line *dl) |
775 | { | 833 | { |
776 | zfree(&dl->line); | 834 | zfree(&dl->line); |
777 | zfree(&dl->name); | 835 | if (dl->ins.ops && dl->ins.ops->free) |
778 | if (dl->ins && dl->ins->ops->free) | 836 | dl->ins.ops->free(&dl->ops); |
779 | dl->ins->ops->free(&dl->ops); | ||
780 | else | 837 | else |
781 | ins__delete(&dl->ops); | 838 | ins__delete(&dl->ops); |
839 | free((void *)dl->ins.name); | ||
840 | dl->ins.name = NULL; | ||
782 | free(dl); | 841 | free(dl); |
783 | } | 842 | } |
784 | 843 | ||
785 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) | 844 | int disasm_line__scnprintf(struct disasm_line *dl, char *bf, size_t size, bool raw) |
786 | { | 845 | { |
787 | if (raw || !dl->ins) | 846 | if (raw || !dl->ins.ops) |
788 | return scnprintf(bf, size, "%-6.6s %s", dl->name, dl->ops.raw); | 847 | return scnprintf(bf, size, "%-6.6s %s", dl->ins.name, dl->ops.raw); |
789 | 848 | ||
790 | return ins__scnprintf(dl->ins, bf, size, &dl->ops); | 849 | return ins__scnprintf(&dl->ins, bf, size, &dl->ops); |
791 | } | 850 | } |
792 | 851 | ||
793 | static void disasm__add(struct list_head *head, struct disasm_line *line) | 852 | 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, | |||
1143 | map__rip_2objdump(map, sym->start); | 1202 | map__rip_2objdump(map, sym->start); |
1144 | 1203 | ||
1145 | /* kcore has no symbols, so add the call target name */ | 1204 | /* kcore has no symbols, so add the call target name */ |
1146 | if (dl->ins && ins__is_call(dl->ins) && !dl->ops.target.name) { | 1205 | if (dl->ins.ops && ins__is_call(&dl->ins) && !dl->ops.target.name) { |
1147 | struct addr_map_symbol target = { | 1206 | struct addr_map_symbol target = { |
1148 | .map = map, | 1207 | .map = map, |
1149 | .addr = dl->ops.target.addr, | 1208 | .addr = dl->ops.target.addr, |
@@ -1173,8 +1232,8 @@ static void delete_last_nop(struct symbol *sym) | |||
1173 | while (!list_empty(list)) { | 1232 | while (!list_empty(list)) { |
1174 | dl = list_entry(list->prev, struct disasm_line, node); | 1233 | dl = list_entry(list->prev, struct disasm_line, node); |
1175 | 1234 | ||
1176 | if (dl->ins && dl->ins->ops) { | 1235 | if (dl->ins.ops) { |
1177 | if (dl->ins->ops != &nop_ops) | 1236 | if (dl->ins.ops != &nop_ops) |
1178 | return; | 1237 | return; |
1179 | } else { | 1238 | } else { |
1180 | if (!strstr(dl->line, " nop ") && | 1239 | if (!strstr(dl->line, " nop ") && |
@@ -1300,6 +1359,14 @@ int symbol__disassemble(struct symbol *sym, struct map *map, const char *arch_na | |||
1300 | if (arch == NULL) | 1359 | if (arch == NULL) |
1301 | return -ENOTSUP; | 1360 | return -ENOTSUP; |
1302 | 1361 | ||
1362 | if (arch->init) { | ||
1363 | err = arch->init(arch); | ||
1364 | if (err) { | ||
1365 | pr_err("%s: failed to initialize %s arch priv area\n", __func__, arch->name); | ||
1366 | return err; | ||
1367 | } | ||
1368 | } | ||
1369 | |||
1303 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, | 1370 | pr_debug("%s: filename=%s, sym=%s, start=%#" PRIx64 ", end=%#" PRIx64 "\n", __func__, |
1304 | symfs_filename, sym->name, map->unmap_ip(map, sym->start), | 1371 | symfs_filename, sym->name, map->unmap_ip(map, sym->start), |
1305 | map->unmap_ip(map, sym->end)); | 1372 | map->unmap_ip(map, sym->end)); |
@@ -1767,7 +1834,7 @@ static size_t disasm_line__fprintf(struct disasm_line *dl, FILE *fp) | |||
1767 | if (dl->offset == -1) | 1834 | if (dl->offset == -1) |
1768 | return fprintf(fp, "%s\n", dl->line); | 1835 | return fprintf(fp, "%s\n", dl->line); |
1769 | 1836 | ||
1770 | printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->name); | 1837 | printed = fprintf(fp, "%#" PRIx64 " %s", dl->offset, dl->ins.name); |
1771 | 1838 | ||
1772 | if (dl->ops.raw[0] != '\0') { | 1839 | if (dl->ops.raw[0] != '\0') { |
1773 | printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", | 1840 | printed += fprintf(fp, "%.*s %s\n", 6 - (int)printed, " ", |
diff --git a/tools/perf/util/annotate.h b/tools/perf/util/annotate.h index 8e490b5c91bc..87e4cadc5d27 100644 --- a/tools/perf/util/annotate.h +++ b/tools/perf/util/annotate.h | |||
@@ -11,7 +11,12 @@ | |||
11 | #include <linux/rbtree.h> | 11 | #include <linux/rbtree.h> |
12 | #include <pthread.h> | 12 | #include <pthread.h> |
13 | 13 | ||
14 | struct ins; | 14 | struct ins_ops; |
15 | |||
16 | struct ins { | ||
17 | const char *name; | ||
18 | struct ins_ops *ops; | ||
19 | }; | ||
15 | 20 | ||
16 | struct ins_operands { | 21 | struct ins_operands { |
17 | char *raw; | 22 | char *raw; |
@@ -28,7 +33,7 @@ struct ins_operands { | |||
28 | u64 addr; | 33 | u64 addr; |
29 | } source; | 34 | } source; |
30 | struct { | 35 | struct { |
31 | struct ins *ins; | 36 | struct ins ins; |
32 | struct ins_operands *ops; | 37 | struct ins_operands *ops; |
33 | } locked; | 38 | } locked; |
34 | }; | 39 | }; |
@@ -43,11 +48,6 @@ struct ins_ops { | |||
43 | struct ins_operands *ops); | 48 | struct ins_operands *ops); |
44 | }; | 49 | }; |
45 | 50 | ||
46 | struct ins { | ||
47 | const char *name; | ||
48 | struct ins_ops *ops; | ||
49 | }; | ||
50 | |||
51 | bool ins__is_jump(const struct ins *ins); | 51 | bool ins__is_jump(const struct ins *ins); |
52 | bool ins__is_call(const struct ins *ins); | 52 | bool ins__is_call(const struct ins *ins); |
53 | bool ins__is_ret(const struct ins *ins); | 53 | bool ins__is_ret(const struct ins *ins); |
@@ -59,8 +59,7 @@ struct disasm_line { | |||
59 | struct list_head node; | 59 | struct list_head node; |
60 | s64 offset; | 60 | s64 offset; |
61 | char *line; | 61 | char *line; |
62 | char *name; | 62 | struct ins ins; |
63 | struct ins *ins; | ||
64 | int line_nr; | 63 | int line_nr; |
65 | float ipc; | 64 | float ipc; |
66 | u64 cycles; | 65 | u64 cycles; |
diff --git a/tools/perf/util/evsel.h b/tools/perf/util/evsel.h index 27fa3a343577..6abb89cd27f9 100644 --- a/tools/perf/util/evsel.h +++ b/tools/perf/util/evsel.h | |||
@@ -392,6 +392,7 @@ int perf_evsel__fprintf(struct perf_evsel *evsel, | |||
392 | #define EVSEL__PRINT_SRCLINE (1<<5) | 392 | #define EVSEL__PRINT_SRCLINE (1<<5) |
393 | #define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6) | 393 | #define EVSEL__PRINT_UNKNOWN_AS_ADDR (1<<6) |
394 | #define EVSEL__PRINT_CALLCHAIN_ARROW (1<<7) | 394 | #define EVSEL__PRINT_CALLCHAIN_ARROW (1<<7) |
395 | #define EVSEL__PRINT_SKIP_IGNORED (1<<8) | ||
395 | 396 | ||
396 | struct callchain_cursor; | 397 | struct callchain_cursor; |
397 | 398 | ||
diff --git a/tools/perf/util/evsel_fprintf.c b/tools/perf/util/evsel_fprintf.c index 53bb614feafb..5a6f52284452 100644 --- a/tools/perf/util/evsel_fprintf.c +++ b/tools/perf/util/evsel_fprintf.c | |||
@@ -109,6 +109,7 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
109 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; | 109 | int print_srcline = print_opts & EVSEL__PRINT_SRCLINE; |
110 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; | 110 | int print_unknown_as_addr = print_opts & EVSEL__PRINT_UNKNOWN_AS_ADDR; |
111 | int print_arrow = print_opts & EVSEL__PRINT_CALLCHAIN_ARROW; | 111 | int print_arrow = print_opts & EVSEL__PRINT_CALLCHAIN_ARROW; |
112 | int print_skip_ignored = print_opts & EVSEL__PRINT_SKIP_IGNORED; | ||
112 | char s = print_oneline ? ' ' : '\t'; | 113 | char s = print_oneline ? ' ' : '\t'; |
113 | bool first = true; | 114 | bool first = true; |
114 | 115 | ||
@@ -124,6 +125,9 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
124 | if (!node) | 125 | if (!node) |
125 | break; | 126 | break; |
126 | 127 | ||
128 | if (node->sym && node->sym->ignore && print_skip_ignored) | ||
129 | goto next; | ||
130 | |||
127 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); | 131 | printed += fprintf(fp, "%-*.*s", left_alignment, left_alignment, " "); |
128 | 132 | ||
129 | if (print_arrow && !first) | 133 | if (print_arrow && !first) |
@@ -162,8 +166,9 @@ int sample__fprintf_callchain(struct perf_sample *sample, int left_alignment, | |||
162 | if (!print_oneline) | 166 | if (!print_oneline) |
163 | printed += fprintf(fp, "\n"); | 167 | printed += fprintf(fp, "\n"); |
164 | 168 | ||
165 | callchain_cursor_advance(cursor); | ||
166 | first = false; | 169 | first = false; |
170 | next: | ||
171 | callchain_cursor_advance(cursor); | ||
167 | } | 172 | } |
168 | } | 173 | } |
169 | 174 | ||
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h index 8091d15113f7..5d4e94061402 100644 --- a/tools/perf/util/probe-event.h +++ b/tools/perf/util/probe-event.h | |||
@@ -18,6 +18,8 @@ struct probe_conf { | |||
18 | extern struct probe_conf probe_conf; | 18 | extern struct probe_conf probe_conf; |
19 | extern bool probe_event_dry_run; | 19 | extern bool probe_event_dry_run; |
20 | 20 | ||
21 | struct symbol; | ||
22 | |||
21 | /* kprobe-tracer and uprobe-tracer tracing point */ | 23 | /* kprobe-tracer and uprobe-tracer tracing point */ |
22 | struct probe_trace_point { | 24 | struct probe_trace_point { |
23 | char *realname; /* function real name (if needed) */ | 25 | char *realname; /* function real name (if needed) */ |
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c index aecff69a510d..420ada9de22f 100644 --- a/tools/perf/util/symbol.c +++ b/tools/perf/util/symbol.c | |||
@@ -1962,7 +1962,7 @@ static bool symbol__read_kptr_restrict(void) | |||
1962 | char line[8]; | 1962 | char line[8]; |
1963 | 1963 | ||
1964 | if (fgets(line, sizeof(line), fp) != NULL) | 1964 | if (fgets(line, sizeof(line), fp) != NULL) |
1965 | value = (geteuid() != 0) ? | 1965 | value = ((geteuid() != 0) || (getuid() != 0)) ? |
1966 | (atoi(line) != 0) : | 1966 | (atoi(line) != 0) : |
1967 | (atoi(line) == 2); | 1967 | (atoi(line) == 2); |
1968 | 1968 | ||
diff --git a/tools/perf/util/symbol.h b/tools/perf/util/symbol.h index dec7e2d44885..1bcbefc0c325 100644 --- a/tools/perf/util/symbol.h +++ b/tools/perf/util/symbol.h | |||
@@ -58,6 +58,7 @@ struct symbol { | |||
58 | u16 namelen; | 58 | u16 namelen; |
59 | u8 binding; | 59 | u8 binding; |
60 | u8 idle:1; | 60 | u8 idle:1; |
61 | u8 ignore:1; | ||
61 | u8 arch_sym; | 62 | u8 arch_sym; |
62 | char name[0]; | 63 | char name[0]; |
63 | }; | 64 | }; |
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c index 5bbd1f609f1f..67ac765da27a 100644 --- a/tools/perf/util/util.c +++ b/tools/perf/util/util.c | |||
@@ -637,12 +637,63 @@ bool find_process(const char *name) | |||
637 | return ret ? false : true; | 637 | return ret ? false : true; |
638 | } | 638 | } |
639 | 639 | ||
640 | static int | ||
641 | fetch_ubuntu_kernel_version(unsigned int *puint) | ||
642 | { | ||
643 | ssize_t len; | ||
644 | size_t line_len = 0; | ||
645 | char *ptr, *line = NULL; | ||
646 | int version, patchlevel, sublevel, err; | ||
647 | FILE *vsig = fopen("/proc/version_signature", "r"); | ||
648 | |||
649 | if (!vsig) { | ||
650 | pr_debug("Open /proc/version_signature failed: %s\n", | ||
651 | strerror(errno)); | ||
652 | return -1; | ||
653 | } | ||
654 | |||
655 | len = getline(&line, &line_len, vsig); | ||
656 | fclose(vsig); | ||
657 | err = -1; | ||
658 | if (len <= 0) { | ||
659 | pr_debug("Reading from /proc/version_signature failed: %s\n", | ||
660 | strerror(errno)); | ||
661 | goto errout; | ||
662 | } | ||
663 | |||
664 | ptr = strrchr(line, ' '); | ||
665 | if (!ptr) { | ||
666 | pr_debug("Parsing /proc/version_signature failed: %s\n", line); | ||
667 | goto errout; | ||
668 | } | ||
669 | |||
670 | err = sscanf(ptr + 1, "%d.%d.%d", | ||
671 | &version, &patchlevel, &sublevel); | ||
672 | if (err != 3) { | ||
673 | pr_debug("Unable to get kernel version from /proc/version_signature '%s'\n", | ||
674 | line); | ||
675 | goto errout; | ||
676 | } | ||
677 | |||
678 | if (puint) | ||
679 | *puint = (version << 16) + (patchlevel << 8) + sublevel; | ||
680 | err = 0; | ||
681 | errout: | ||
682 | free(line); | ||
683 | return err; | ||
684 | } | ||
685 | |||
640 | int | 686 | int |
641 | fetch_kernel_version(unsigned int *puint, char *str, | 687 | fetch_kernel_version(unsigned int *puint, char *str, |
642 | size_t str_size) | 688 | size_t str_size) |
643 | { | 689 | { |
644 | struct utsname utsname; | 690 | struct utsname utsname; |
645 | int version, patchlevel, sublevel, err; | 691 | int version, patchlevel, sublevel, err; |
692 | bool int_ver_ready = false; | ||
693 | |||
694 | if (access("/proc/version_signature", R_OK) == 0) | ||
695 | if (!fetch_ubuntu_kernel_version(puint)) | ||
696 | int_ver_ready = true; | ||
646 | 697 | ||
647 | if (uname(&utsname)) | 698 | if (uname(&utsname)) |
648 | return -1; | 699 | return -1; |
@@ -656,12 +707,12 @@ fetch_kernel_version(unsigned int *puint, char *str, | |||
656 | &version, &patchlevel, &sublevel); | 707 | &version, &patchlevel, &sublevel); |
657 | 708 | ||
658 | if (err != 3) { | 709 | if (err != 3) { |
659 | pr_debug("Unablt to get kernel version from uname '%s'\n", | 710 | pr_debug("Unable to get kernel version from uname '%s'\n", |
660 | utsname.release); | 711 | utsname.release); |
661 | return -1; | 712 | return -1; |
662 | } | 713 | } |
663 | 714 | ||
664 | if (puint) | 715 | if (puint && !int_ver_ready) |
665 | *puint = (version << 16) + (patchlevel << 8) + sublevel; | 716 | *puint = (version << 16) + (patchlevel << 8) + sublevel; |
666 | return 0; | 717 | return 0; |
667 | } | 718 | } |