Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

perf tests code-reading: Handle change in objdump output from binutils >= 2.41 on riscv

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>

authored by

Charlie Jenkins and committed by
Arnaldo Carvalho de Melo
0f9ad973 058b38cc

+91 -1
+91 -1
tools/perf/tests/code-reading.c
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 #include <errno.h> 3 + #include <linux/kconfig.h> 3 4 #include <linux/kernel.h> 4 5 #include <linux/types.h> 5 6 #include <inttypes.h> ··· 9 8 #include <stdio.h> 10 9 #include <string.h> 11 10 #include <sys/param.h> 11 + #include <sys/utsname.h> 12 12 #include <perf/cpumap.h> 13 13 #include <perf/evlist.h> 14 14 #include <perf/mmap.h> ··· 178 176 return err; 179 177 } 180 178 179 + /* 180 + * Only gets GNU objdump version. Returns 0 for llvm-objdump. 181 + */ 182 + static int objdump_version(void) 183 + { 184 + size_t line_len; 185 + char cmd[PATH_MAX * 2]; 186 + char *line = NULL; 187 + const char *fmt; 188 + FILE *f; 189 + int ret; 190 + 191 + int version_tmp, version_num = 0; 192 + char *version = 0, *token; 193 + 194 + fmt = "%s --version"; 195 + ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path); 196 + if (ret <= 0 || (size_t)ret >= sizeof(cmd)) 197 + return -1; 198 + /* Ignore objdump errors */ 199 + strcat(cmd, " 2>/dev/null"); 200 + f = popen(cmd, "r"); 201 + if (!f) { 202 + pr_debug("popen failed\n"); 203 + return -1; 204 + } 205 + /* Get first line of objdump --version output */ 206 + ret = getline(&line, &line_len, f); 207 + pclose(f); 208 + if (ret < 0) { 209 + pr_debug("getline failed\n"); 210 + return -1; 211 + } 212 + 213 + token = strsep(&line, " "); 214 + if (token != NULL && !strcmp(token, "GNU")) { 215 + // version is last part of first line of objdump --version output. 216 + while ((token = strsep(&line, " "))) 217 + version = token; 218 + 219 + // Convert version into a format we can compare with 220 + token = strsep(&version, "."); 221 + version_num = atoi(token); 222 + if (version_num) 223 + version_num *= 10000; 224 + 225 + token = strsep(&version, "."); 226 + version_tmp = atoi(token); 227 + if (token) 228 + version_num += version_tmp * 100; 229 + 230 + token = strsep(&version, "."); 231 + version_tmp = atoi(token); 232 + if (token) 233 + version_num += version_tmp; 234 + } 235 + 236 + return version_num; 237 + } 238 + 181 239 static int read_via_objdump(const char *filename, u64 addr, void *buf, 182 240 size_t len) 183 241 { 242 + u64 stop_address = addr + len; 243 + struct utsname uname_buf; 184 244 char cmd[PATH_MAX * 2]; 185 245 const char *fmt; 186 246 FILE *f; 187 247 int ret; 188 248 249 + ret = uname(&uname_buf); 250 + if (ret) { 251 + pr_debug("uname failed\n"); 252 + return -1; 253 + } 254 + 255 + if (!strncmp(uname_buf.machine, "riscv", 5)) { 256 + int version = objdump_version(); 257 + 258 + /* Default to this workaround if version parsing fails */ 259 + if (version < 0 || version > 24100) { 260 + /* 261 + * Starting at riscv objdump version 2.41, dumping in 262 + * the middle of an instruction is not supported. riscv 263 + * instructions are aligned along 2-byte intervals and 264 + * can be either 2-bytes or 4-bytes. This makes it 265 + * possible that the stop-address lands in the middle of 266 + * a 4-byte instruction. Increase the stop_address by 267 + * two to ensure an instruction is not cut in half, but 268 + * leave the len as-is so only the expected number of 269 + * bytes are collected. 270 + */ 271 + stop_address += 2; 272 + } 273 + } 274 + 189 275 fmt = "%s -z -d --start-address=0x%"PRIx64" --stop-address=0x%"PRIx64" %s"; 190 - ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path, addr, addr + len, 276 + ret = snprintf(cmd, sizeof(cmd), fmt, test_objdump_path, addr, stop_address, 191 277 filename); 192 278 if (ret <= 0 || (size_t)ret >= sizeof(cmd)) 193 279 return -1;