]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
perf tests code-reading: Handle change in objdump output from binutils >= 2.41 on...
authorCharlie Jenkins <charlie@rivosinc.com>
Thu, 19 Dec 2024 21:44:25 +0000 (13:44 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 8 Jan 2025 20:20:42 +0000 (17:20 -0300)
After binutils commit e43d876 which was first included in binutils 2.41,
riscv no longer supports dumping in the middle of instructions.

Increase the objdump window by 2-bytes to ensure that any instruction
that sits on the boundary of the specified stop-address is not cut in
half.

Signed-off-by: Charlie Jenkins <charlie@rivosinc.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Bill Wendling <morbo@google.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Justin Stitt <justinstitt@google.com>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Nathan Chancellor <nathan@kernel.org>
Cc: Nick Desaulniers <ndesaulniers@google.com>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <paul.walmsley@sifive.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: linux-riscv@lists.infradead.org
Link: https://lore.kernel.org/r/20241219-perf_fix_riscv_obj_reading-v3-1-a7d644dcfa50@rivosinc.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
tools/perf/tests/code-reading.c

index 27c82cfb7e7de42284bf5af9cf7594a3a963052e..b1abb34d7818d8a9ac963e2185ec3e7d9bf8d387 100644 (file)
@@ -1,5 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0
 #include <errno.h>
+#include <linux/kconfig.h>
 #include <linux/kernel.h>
 #include <linux/types.h>
 #include <inttypes.h>
@@ -8,6 +9,7 @@
 #include <stdio.h>
 #include <string.h>
 #include <sys/param.h>
+#include <sys/utsname.h>
 #include <perf/cpumap.h>
 #include <perf/evlist.h>
 #include <perf/mmap.h>
@@ -176,16 +178,104 @@ static int read_objdump_output(FILE *f, void *buf, size_t *len, u64 start_addr)
        return err;
 }
 
+/*
+ * Only gets GNU objdump version. Returns 0 for llvm-objdump.
+ */
+static int objdump_version(void)
+{
+       size_t line_len;
+       char cmd[PATH_MAX * 2];
+       char *line = NULL;
+       const char *fmt;
+       FILE *f;
+       int ret;
+
+       int version_tmp, version_num = 0;
+       char *version = 0, *token;
+
+       fmt = "%s --version";
+       ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path);
+       if (ret <= 0 || (size_t)ret >= sizeof(cmd))
+               return -1;
+       /* Ignore objdump errors */
+       strcat(cmd, " 2>/dev/null");
+       f = popen(cmd, "r");
+       if (!f) {
+               pr_debug("popen failed\n");
+               return -1;
+       }
+       /* Get first line of objdump --version output */
+       ret = getline(&line, &line_len, f);
+       pclose(f);
+       if (ret < 0) {
+               pr_debug("getline failed\n");
+               return -1;
+       }
+
+       token = strsep(&line, " ");
+       if (token != NULL && !strcmp(token, "GNU")) {
+               // version is last part of first line of objdump --version output.
+               while ((token = strsep(&line, " ")))
+                       version = token;
+
+               // Convert version into a format we can compare with
+               token = strsep(&version, ".");
+               version_num = atoi(token);
+               if (version_num)
+                       version_num *= 10000;
+
+               token = strsep(&version, ".");
+               version_tmp = atoi(token);
+               if (token)
+                       version_num += version_tmp * 100;
+
+               token = strsep(&version, ".");
+               version_tmp = atoi(token);
+               if (token)
+                       version_num += version_tmp;
+       }
+
+       return version_num;
+}
+
 static int read_via_objdump(const char *filename, u64 addr, void *buf,
                            size_t len)
 {
+       u64 stop_address = addr + len;
+       struct utsname uname_buf;
        char cmd[PATH_MAX * 2];
        const char *fmt;
        FILE *f;
        int ret;
 
+       ret = uname(&uname_buf);
+       if (ret) {
+               pr_debug("uname failed\n");
+               return -1;
+       }
+
+       if (!strncmp(uname_buf.machine, "riscv", 5)) {
+               int version = objdump_version();
+
+               /* Default to this workaround if version parsing fails */
+               if (version < 0 || version > 24100) {
+                       /*
+                        * Starting at riscv objdump version 2.41, dumping in
+                        * the middle of an instruction is not supported. riscv
+                        * instructions are aligned along 2-byte intervals and
+                        * can be either 2-bytes or 4-bytes. This makes it
+                        * possible that the stop-address lands in the middle of
+                        * a 4-byte instruction. Increase the stop_address by
+                        * two to ensure an instruction is not cut in half, but
+                        * leave the len as-is so only the expected number of
+                        * bytes are collected.
+                        */
+                       stop_address += 2;
+               }
+       }
+
        fmt = "%s -z -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s";
-       ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path, addr, addr + len,
+       ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path, addr, stop_address,
                       filename);
        if (ret <= 0 || (size_t)ret >= sizeof(cmd))
                return -1;