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

perf tools: Try chroot'ed filename when opening dso/symbol

Currently it doesn't handle tasks in chroot properly. As filenames in
MMAP records base on their root directory, it's different than what perf
tool can see from outside.

Add filename_with_chroot() helper to deal with those cases. The
function returns a new filename only if it's in a different root
directory. Since it needs to access /proc for the process, it only
works until the task exits.

With this change, I can see symbols in my program like below.

# perf record -o- chroot myroot myprog 3 | perf report -i-
...
#
# Overhead Command Shared Object Symbol
# ........ ....... ................. .............................
#
99.83% myprog myprog [.] loop
0.04% chroot [kernel.kallsyms] [k] fxregs_fixup
0.04% chroot [kernel.kallsyms] [k] rsm_load_seg_32
...

Signed-off-by: Namhyung Kim <namhyung@kernel.org>
Acked-by: Jiri Olsa <jolsa@kernel.org>
Cc: Andi Kleen <ak@linux.intel.com>
Cc: Ian Rogers <irogers@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Link: http://lore.kernel.org/lkml/20220202070828.143303-3-namhyung@kernel.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>

authored by

Namhyung Kim and committed by
Arnaldo Carvalho de Melo
67fd1892 e3c85076

+69 -2
+13 -2
tools/perf/util/dso.c
··· 508 508 root_dir, name, PATH_MAX)) 509 509 goto out; 510 510 511 - if (!is_regular_file(name)) 512 - goto out; 511 + if (!is_regular_file(name)) { 512 + char *new_name; 513 + 514 + if (errno != ENOENT || dso->nsinfo == NULL) 515 + goto out; 516 + 517 + new_name = filename_with_chroot(dso->nsinfo->pid, name); 518 + if (!new_name) 519 + goto out; 520 + 521 + free(name); 522 + name = new_name; 523 + } 513 524 514 525 if (dso__needs_decompress(dso)) { 515 526 char newpath[KMOD_DECOMP_LEN];
+13
tools/perf/util/dsos.c
··· 2 2 #include "debug.h" 3 3 #include "dsos.h" 4 4 #include "dso.h" 5 + #include "util.h" 5 6 #include "vdso.h" 6 7 #include "namespaces.h" 8 + #include <errno.h> 7 9 #include <libgen.h> 8 10 #include <stdlib.h> 9 11 #include <string.h> 10 12 #include <symbol.h> // filename__read_build_id 13 + #include <unistd.h> 11 14 12 15 static int __dso_id__cmp(struct dso_id *a, struct dso_id *b) 13 16 { ··· 79 76 if (filename__read_build_id(pos->long_name, &pos->bid) > 0) { 80 77 have_build_id = true; 81 78 pos->has_build_id = true; 79 + } else if (errno == ENOENT && pos->nsinfo) { 80 + char *new_name = filename_with_chroot(pos->nsinfo->pid, 81 + pos->long_name); 82 + 83 + if (new_name && filename__read_build_id(new_name, 84 + &pos->bid) > 0) { 85 + have_build_id = true; 86 + pos->has_build_id = true; 87 + } 88 + free(new_name); 82 89 } 83 90 nsinfo__mountns_exit(&nsc); 84 91 }
+10
tools/perf/util/symbol.c
··· 1864 1864 nsinfo__mountns_exit(&nsc); 1865 1865 1866 1866 is_reg = is_regular_file(name); 1867 + if (!is_reg && errno == ENOENT && dso->nsinfo) { 1868 + char *new_name = filename_with_chroot(dso->nsinfo->pid, 1869 + name); 1870 + if (new_name) { 1871 + is_reg = is_regular_file(new_name); 1872 + strlcpy(name, new_name, PATH_MAX); 1873 + free(new_name); 1874 + } 1875 + } 1876 + 1867 1877 #ifdef HAVE_LIBBFD_SUPPORT 1868 1878 if (is_reg) 1869 1879 bfdrc = dso__load_bfd_symbols(dso, name);
+31
tools/perf/util/util.c
··· 431 431 432 432 pr_debug("DEBUGINFOD_URLS=%s\n", getenv("DEBUGINFOD_URLS")); 433 433 } 434 + 435 + /* 436 + * Return a new filename prepended with task's root directory if it's in 437 + * a chroot. Callers should free the returned string. 438 + */ 439 + char *filename_with_chroot(int pid, const char *filename) 440 + { 441 + char buf[PATH_MAX]; 442 + char proc_root[32]; 443 + char *new_name = NULL; 444 + int ret; 445 + 446 + scnprintf(proc_root, sizeof(proc_root), "/proc/%d/root", pid); 447 + ret = readlink(proc_root, buf, sizeof(buf) - 1); 448 + if (ret <= 0) 449 + return NULL; 450 + 451 + /* readlink(2) does not append a null byte to buf */ 452 + buf[ret] = '\0'; 453 + 454 + if (!strcmp(buf, "/")) 455 + return NULL; 456 + 457 + if (strstr(buf, "(deleted)")) 458 + return NULL; 459 + 460 + if (asprintf(&new_name, "%s/%s", buf, filename) < 0) 461 + return NULL; 462 + 463 + return new_name; 464 + }
+2
tools/perf/util/util.h
··· 77 77 bool set; 78 78 }; 79 79 void perf_debuginfod_setup(struct perf_debuginfod *di); 80 + 81 + char *filename_with_chroot(int pid, const char *filename); 80 82 #endif /* GIT_COMPAT_UTIL_H */