perf annotate: Fix instruction association and parsing for LoongArch
authorWANG Rui <wangrui@loongson.cn>
Tue, 20 Jun 2023 13:20:25 +0000 (21:20 +0800)
committerNamhyung Kim <namhyung@kernel.org>
Wed, 21 Jun 2023 04:55:06 +0000 (21:55 -0700)
In the perf annotate view for LoongArch, there is no arrowed line
pointing to the target from the branch instruction. This issue is
caused by incorrect instruction association and parsing.

$ perf record alloc-6276705c94ad1398 # rust benchmark
$ perf report

  0.28 │       ori        $a1, $zero, 0x63
       │       move       $a2, $zero
 10.55 │       addi.d     $a3, $a2, 1(0x1)
       │       sltu       $a4, $a3, $s7
  9.53 │       masknez    $a4, $s7, $a4
       │       sub.d      $a3, $a3, $a4
 12.12 │       st.d       $a1, $fp, 24(0x18)
       │       st.d       $a3, $fp, 16(0x10)
 16.29 │       slli.d     $a2, $a2, 0x2
       │       ldx.w      $a2, $s8, $a2
 12.77 │       st.w       $a2, $sp, 724(0x2d4)
       │       st.w       $s0, $sp, 720(0x2d0)
  7.03 │       addi.d     $a2, $sp, 720(0x2d0)
       │       addi.d     $a1, $a1, -1(0xfff)
 12.03 │       move       $a2, $a3
       │     → bne        $a1, $s3, -52(0x3ffcc)  # 82ce8 <test::bench::Bencher::iter+0x3f4>
  2.50 │       addi.d     $a0, $a0, 1(0x1)

This patch fixes instruction association issues, such as associating
branch instructions with jump_ops instead of call_ops, and corrects
false instruction matches. It also implements branch instruction parsing
specifically for LoongArch. With this patch, we will be able to see the
arrowed line.

  0.79 │3ec:   ori        $a1, $zero, 0x63
       │       move       $a2, $zero
 10.32 │3f4:┌─→addi.d     $a3, $a2, 1(0x1)
       │    │  sltu       $a4, $a3, $s7
 10.44 │    │  masknez    $a4, $s7, $a4
       │    │  sub.d      $a3, $a3, $a4
 14.17 │    │  st.d       $a1, $fp, 24(0x18)
       │    │  st.d       $a3, $fp, 16(0x10)
 13.15 │    │  slli.d     $a2, $a2, 0x2
       │    │  ldx.w      $a2, $s8, $a2
 11.00 │    │  st.w       $a2, $sp, 724(0x2d4)
       │    │  st.w       $s0, $sp, 720(0x2d0)
  8.00 │    │  addi.d     $a2, $sp, 720(0x2d0)
       │    │  addi.d     $a1, $a1, -1(0xfff)
 11.99 │    │  move       $a2, $a3
       │    └──bne        $a1, $s3, 3f4
  3.17 │       addi.d     $a0, $a0, 1(0x1)

Signed-off-by: WANG Rui <wangrui@loongson.cn>
Acked-by: Namhyung Kim <namhyung@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Arnaldo Carvalho de Melo <acme@kernel.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: loongarch@lists.linux.dev
Cc: loongson-kernel@lists.loongnix.cn
Cc: Huacai Chen <chenhuacai@loongson.cn>
Cc: Tiezhu Yang <yangtiezhu@loongson.cn>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: WANG Xuerui <kernel@xen0n.name>
Link: https://lore.kernel.org/r/20230620132025.105563-1-wangrui@loongson.cn
Signed-off-by: Namhyung Kim <namhyung@kernel.org>
tools/perf/arch/loongarch/annotate/instructions.c
tools/perf/arch/s390/annotate/instructions.c
tools/perf/util/annotate.c

index ab21bf1..98e19c5 100644 (file)
  * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
  */
 
+static int loongarch_call__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
+{
+       char *c, *endptr, *tok, *name;
+       struct map *map = ms->map;
+       struct addr_map_symbol target = {
+               .ms = { .map = map, },
+       };
+
+       c = strchr(ops->raw, '#');
+       if (c++ == NULL)
+               return -1;
+
+       ops->target.addr = strtoull(c, &endptr, 16);
+
+       name = strchr(endptr, '<');
+       name++;
+
+       if (arch->objdump.skip_functions_char &&
+           strchr(name, arch->objdump.skip_functions_char))
+               return -1;
+
+       tok = strchr(name, '>');
+       if (tok == NULL)
+               return -1;
+
+       *tok = '\0';
+       ops->target.name = strdup(name);
+       *tok = '>';
+
+       if (ops->target.name == NULL)
+               return -1;
+
+       target.addr = map__objdump_2mem(map, ops->target.addr);
+
+       if (maps__find_ams(ms->maps, &target) == 0 &&
+           map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
+               ops->target.sym = target.ms.sym;
+
+       return 0;
+}
+
+static struct ins_ops loongarch_call_ops = {
+       .parse     = loongarch_call__parse,
+       .scnprintf = call__scnprintf,
+};
+
+static int loongarch_jump__parse(struct arch *arch, struct ins_operands *ops, struct map_symbol *ms)
+{
+       struct map *map = ms->map;
+       struct symbol *sym = ms->sym;
+       struct addr_map_symbol target = {
+               .ms = { .map = map, },
+       };
+       const char *c = strchr(ops->raw, '#');
+       u64 start, end;
+
+       ops->raw_comment = strchr(ops->raw, arch->objdump.comment_char);
+       ops->raw_func_start = strchr(ops->raw, '<');
+
+       if (ops->raw_func_start && c > ops->raw_func_start)
+               c = NULL;
+
+       if (c++ != NULL)
+               ops->target.addr = strtoull(c, NULL, 16);
+       else
+               ops->target.addr = strtoull(ops->raw, NULL, 16);
+
+       target.addr = map__objdump_2mem(map, ops->target.addr);
+       start = map__unmap_ip(map, sym->start);
+       end = map__unmap_ip(map, sym->end);
+
+       ops->target.outside = target.addr < start || target.addr > end;
+
+       if (maps__find_ams(ms->maps, &target) == 0 &&
+           map__rip_2objdump(target.ms.map, map__map_ip(target.ms.map, target.addr)) == ops->target.addr)
+               ops->target.sym = target.ms.sym;
+
+       if (!ops->target.outside) {
+               ops->target.offset = target.addr - start;
+               ops->target.offset_avail = true;
+       } else {
+               ops->target.offset_avail = false;
+       }
+
+       return 0;
+}
+
+static struct ins_ops loongarch_jump_ops = {
+       .parse     = loongarch_jump__parse,
+       .scnprintf = jump__scnprintf,
+};
+
 static
 struct ins_ops *loongarch__associate_ins_ops(struct arch *arch, const char *name)
 {
        struct ins_ops *ops = NULL;
 
-       if (!strncmp(name, "beqz", 4) ||
-           !strncmp(name, "bnez", 4) ||
-           !strncmp(name, "beq", 3) ||
-           !strncmp(name, "bne", 3) ||
-           !strncmp(name, "blt", 3) ||
-           !strncmp(name, "bge", 3) ||
-           !strncmp(name, "bltu", 4) ||
-           !strncmp(name, "bgeu", 4) ||
-           !strncmp(name, "bl", 2))
-               ops = &call_ops;
-       else if (!strncmp(name, "jirl", 4))
+       if (!strcmp(name, "bl"))
+               ops = &loongarch_call_ops;
+       else if (!strcmp(name, "jirl"))
                ops = &ret_ops;
-       else if (name[0] == 'b')
-               ops = &jump_ops;
+       else if (!strcmp(name, "b") ||
+                !strncmp(name, "beq", 3) ||
+                !strncmp(name, "bne", 3) ||
+                !strncmp(name, "blt", 3) ||
+                !strncmp(name, "bge", 3) ||
+                !strncmp(name, "bltu", 4) ||
+                !strncmp(name, "bgeu", 4))
+               ops = &loongarch_jump_ops;
        else
                return NULL;
 
index de925b0..da5aa3e 100644 (file)
@@ -45,9 +45,6 @@ static int s390_call__parse(struct arch *arch, struct ins_operands *ops,
        return 0;
 }
 
-static int call__scnprintf(struct ins *ins, char *bf, size_t size,
-                          struct ins_operands *ops, int max_ins_name);
-
 static struct ins_ops s390_call_ops = {
        .parse     = s390_call__parse,
        .scnprintf = call__scnprintf,
index 77c8164..ba988a1 100644 (file)
@@ -62,6 +62,10 @@ static regex_t        file_lineno;
 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);
+static int call__scnprintf(struct ins *ins, char *bf, size_t size,
+                         struct ins_operands *ops, int max_ins_name);
+static int jump__scnprintf(struct ins *ins, char *bf, size_t size,
+                         struct ins_operands *ops, int max_ins_name);
 
 struct arch {
        const char      *name;
@@ -324,7 +328,7 @@ static struct ins_ops call_ops = {
 
 bool ins__is_call(const struct ins *ins)
 {
-       return ins->ops == &call_ops || ins->ops == &s390_call_ops;
+       return ins->ops == &call_ops || ins->ops == &s390_call_ops || ins->ops == &loongarch_call_ops;
 }
 
 /*
@@ -465,7 +469,7 @@ static struct ins_ops jump_ops = {
 
 bool ins__is_jump(const struct ins *ins)
 {
-       return ins->ops == &jump_ops;
+       return ins->ops == &jump_ops || ins->ops == &loongarch_jump_ops;
 }
 
 static int comment__symbol(char *raw, char *comment, u64 *addrp, char **namep)