diff options
author | Krister Johansen <kjlx@templeofstupid.com> | 2017-07-05 18:48:08 -0700 |
---|---|---|
committer | Arnaldo Carvalho de Melo <acme@redhat.com> | 2017-07-18 23:14:09 -0300 |
commit | 843ff37bb59edbe51d64e77ba1b3245a15a4dd9f (patch) | |
tree | 7c1e50cb4b60af9776364ec65118c9a0db0cbcdf /tools/perf/util/namespaces.c | |
parent | 86bcdb5a43997bb02ba25a76482c7bfc652ba45b (diff) |
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 <kjlx@templeofstupid.com>
Tested-by: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas-Mich Richter <tmricht@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1499305693-1599-2-git-send-email-kjlx@templeofstupid.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Diffstat (limited to 'tools/perf/util/namespaces.c')
-rw-r--r-- | tools/perf/util/namespaces.c | 127 |
1 files changed, 127 insertions, 0 deletions
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 <sys/types.h> +#include <sys/stat.h> +#include <sched.h> #include <stdlib.h> #include <stdio.h> #include <string.h> +#include <unistd.h> 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; + } +} |