perf tools: Try chroot'ed filename when opening dso/symbol
authorNamhyung Kim <namhyung@kernel.org>
Wed, 2 Feb 2022 07:08:26 +0000 (23:08 -0800)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Thu, 10 Feb 2022 18:32:25 +0000 (15:32 -0300)
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>
tools/perf/util/dso.c
tools/perf/util/dsos.c
tools/perf/util/symbol.c
tools/perf/util/util.c
tools/perf/util/util.h

index 9cc8a17..5ac1395 100644 (file)
@@ -508,8 +508,19 @@ static int __open_dso(struct dso *dso, struct machine *machine)
                                            root_dir, name, PATH_MAX))
                goto out;
 
-       if (!is_regular_file(name))
-               goto out;
+       if (!is_regular_file(name)) {
+               char *new_name;
+
+               if (errno != ENOENT || dso->nsinfo == NULL)
+                       goto out;
+
+               new_name = filename_with_chroot(dso->nsinfo->pid, name);
+               if (!new_name)
+                       goto out;
+
+               free(name);
+               name = new_name;
+       }
 
        if (dso__needs_decompress(dso)) {
                char newpath[KMOD_DECOMP_LEN];
index 183a81d..b97366f 100644 (file)
@@ -2,12 +2,15 @@
 #include "debug.h"
 #include "dsos.h"
 #include "dso.h"
+#include "util.h"
 #include "vdso.h"
 #include "namespaces.h"
+#include <errno.h>
 #include <libgen.h>
 #include <stdlib.h>
 #include <string.h>
 #include <symbol.h> // filename__read_build_id
+#include <unistd.h>
 
 static int __dso_id__cmp(struct dso_id *a, struct dso_id *b)
 {
@@ -76,6 +79,16 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
                if (filename__read_build_id(pos->long_name, &pos->bid) > 0) {
                        have_build_id     = true;
                        pos->has_build_id = true;
+               } else if (errno == ENOENT && pos->nsinfo) {
+                       char *new_name = filename_with_chroot(pos->nsinfo->pid,
+                                                             pos->long_name);
+
+                       if (new_name && filename__read_build_id(new_name,
+                                                               &pos->bid) > 0) {
+                               have_build_id = true;
+                               pos->has_build_id = true;
+                       }
+                       free(new_name);
                }
                nsinfo__mountns_exit(&nsc);
        }
index b2ed314..34302c6 100644 (file)
@@ -1864,6 +1864,16 @@ int dso__load(struct dso *dso, struct map *map)
                        nsinfo__mountns_exit(&nsc);
 
                is_reg = is_regular_file(name);
+               if (!is_reg && errno == ENOENT && dso->nsinfo) {
+                       char *new_name = filename_with_chroot(dso->nsinfo->pid,
+                                                             name);
+                       if (new_name) {
+                               is_reg = is_regular_file(new_name);
+                               strlcpy(name, new_name, PATH_MAX);
+                               free(new_name);
+                       }
+               }
+
 #ifdef HAVE_LIBBFD_SUPPORT
                if (is_reg)
                        bfdrc = dso__load_bfd_symbols(dso, name);
index fb4f661..f8571a6 100644 (file)
@@ -431,3 +431,34 @@ void perf_debuginfod_setup(struct perf_debuginfod *di)
 
        pr_debug("DEBUGINFOD_URLS=%s\n", getenv("DEBUGINFOD_URLS"));
 }
+
+/*
+ * Return a new filename prepended with task's root directory if it's in
+ * a chroot.  Callers should free the returned string.
+ */
+char *filename_with_chroot(int pid, const char *filename)
+{
+       char buf[PATH_MAX];
+       char proc_root[32];
+       char *new_name = NULL;
+       int ret;
+
+       scnprintf(proc_root, sizeof(proc_root), "/proc/%d/root", pid);
+       ret = readlink(proc_root, buf, sizeof(buf) - 1);
+       if (ret <= 0)
+               return NULL;
+
+       /* readlink(2) does not append a null byte to buf */
+       buf[ret] = '\0';
+
+       if (!strcmp(buf, "/"))
+               return NULL;
+
+       if (strstr(buf, "(deleted)"))
+               return NULL;
+
+       if (asprintf(&new_name, "%s/%s", buf, filename) < 0)
+               return NULL;
+
+       return new_name;
+}
index 7b625cb..0f78f1e 100644 (file)
@@ -77,4 +77,6 @@ struct perf_debuginfod {
        bool             set;
 };
 void perf_debuginfod_setup(struct perf_debuginfod *di);
+
+char *filename_with_chroot(int pid, const char *filename);
 #endif /* GIT_COMPAT_UTIL_H */