From 843ff37bb59edbe51d64e77ba1b3245a15a4dd9f Mon Sep 17 00:00:00 2001 From: Krister Johansen Date: Wed, 5 Jul 2017 18:48:08 -0700 Subject: perf symbols: Find symbols in different mount namespace Teach perf how to resolve symbols from binaries that are in a different mount namespace from the tool. This allows perf to generate meaningful stack traces even if the binary resides in a different mount namespace from the tool. Signed-off-by: Krister Johansen Tested-by: Brendan Gregg Cc: Alexander Shishkin Cc: Peter Zijlstra Cc: Thomas-Mich Richter Link: http://lkml.kernel.org/r/1499305693-1599-2-git-send-email-kjlx@templeofstupid.com Signed-off-by: Arnaldo Carvalho de Melo --- tools/perf/util/namespaces.c | 127 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) (limited to 'tools/perf/util/namespaces.c') diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c index 67dcbcc73c7d..bcc6bb19cf10 100644 --- a/tools/perf/util/namespaces.c +++ b/tools/perf/util/namespaces.c @@ -9,9 +9,13 @@ #include "namespaces.h" #include "util.h" #include "event.h" +#include +#include +#include #include #include #include +#include struct namespaces *namespaces__new(struct namespaces_event *event) { @@ -35,3 +39,126 @@ void namespaces__free(struct namespaces *namespaces) { free(namespaces); } + +void nsinfo__init(struct nsinfo *nsi) +{ + char oldns[PATH_MAX]; + char *newns = NULL; + struct stat old_stat; + struct stat new_stat; + + if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) + return; + + if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1) + return; + + if (stat(oldns, &old_stat) < 0) + goto out; + + if (stat(newns, &new_stat) < 0) + goto out; + + /* Check if the mount namespaces differ, if so then indicate that we + * want to switch as part of looking up dso/map data. + */ + if (old_stat.st_ino != new_stat.st_ino) { + nsi->need_setns = true; + nsi->mntns_path = newns; + newns = NULL; + } + +out: + free(newns); +} + +struct nsinfo *nsinfo__new(pid_t pid) +{ + struct nsinfo *nsi = calloc(1, sizeof(*nsi)); + + if (nsi != NULL) { + nsi->pid = pid; + nsi->need_setns = false; + nsinfo__init(nsi); + refcount_set(&nsi->refcnt, 1); + } + + return nsi; +} + +void nsinfo__delete(struct nsinfo *nsi) +{ + zfree(&nsi->mntns_path); + free(nsi); +} + +struct nsinfo *nsinfo__get(struct nsinfo *nsi) +{ + if (nsi) + refcount_inc(&nsi->refcnt); + return nsi; +} + +void nsinfo__put(struct nsinfo *nsi) +{ + if (nsi && refcount_dec_and_test(&nsi->refcnt)) + nsinfo__delete(nsi); +} + +void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc) +{ + char curpath[PATH_MAX]; + int oldns = -1; + int newns = -1; + + if (nc == NULL) + return; + + nc->oldns = -1; + nc->newns = -1; + + if (!nsi || !nsi->need_setns) + return; + + if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX) + return; + + oldns = open(curpath, O_RDONLY); + if (oldns < 0) + return; + + newns = open(nsi->mntns_path, O_RDONLY); + if (newns < 0) + goto errout; + + if (setns(newns, CLONE_NEWNS) < 0) + goto errout; + + nc->oldns = oldns; + nc->newns = newns; + return; + +errout: + if (oldns > -1) + close(oldns); + if (newns > -1) + close(newns); +} + +void nsinfo__mountns_exit(struct nscookie *nc) +{ + if (nc == NULL || nc->oldns == -1 || nc->newns == -1) + return; + + setns(nc->oldns, CLONE_NEWNS); + + if (nc->oldns > -1) { + close(nc->oldns); + nc->oldns = -1; + } + + if (nc->newns > -1) { + close(nc->newns); + nc->newns = -1; + } +} -- cgit v1.2.3