perf symbols: Find symbols in different mount namespace
authorKrister Johansen <kjlx@templeofstupid.com>
Thu, 6 Jul 2017 01:48:08 +0000 (18:48 -0700)
committerArnaldo Carvalho de Melo <acme@redhat.com>
Wed, 19 Jul 2017 02:14:09 +0000 (23:14 -0300)
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>
tools/perf/util/dso.c
tools/perf/util/dso.h
tools/perf/util/map.c
tools/perf/util/namespaces.c
tools/perf/util/namespaces.h
tools/perf/util/symbol.c
tools/perf/util/thread.c
tools/perf/util/thread.h

index 4e7ab611377acd56c1be78cbd983058e5ce5142b..beda40ed63b0d7b7b0caf401370efeda06c39dcb 100644 (file)
@@ -1236,6 +1236,7 @@ void dso__delete(struct dso *dso)
        dso_cache__free(dso);
        dso__free_a2l(dso);
        zfree(&dso->symsrc_filename);
+       nsinfo__zput(dso->nsinfo);
        pthread_mutex_destroy(&dso->lock);
        free(dso);
 }
index bd061ba7b47cc8eab2ff05e4027dc22990d7c7ef..78ec637fc68b29b06fab8c3e2a23f07bb59a1eb9 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/types.h>
 #include <linux/bitops.h>
 #include "map.h"
+#include "namespaces.h"
 #include "build-id.h"
 
 enum dso_binary_type {
@@ -187,6 +188,7 @@ struct dso {
                void     *priv;
                u64      db_id;
        };
+       struct nsinfo   *nsinfo;
        refcount_t       refcnt;
        char             name[0];
 };
index 2179b2deb7307c749fb171e48c784e4bc158b028..5dc60ca5a29413e480a4af1af01ae162969361bb 100644 (file)
@@ -16,6 +16,7 @@
 #include "machine.h"
 #include <linux/string.h>
 #include "srcline.h"
+#include "namespaces.h"
 #include "unwind.h"
 
 static void __maps__insert(struct maps *maps, struct map *map);
@@ -200,6 +201,7 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
                        if (type != MAP__FUNCTION)
                                dso__set_loaded(dso, map->type);
                }
+               dso->nsinfo = nsinfo__get(thread->nsinfo);
                dso__put(dso);
        }
        return map;
index 67dcbcc73c7dcdd95da8af4d45e3257d38123147..bcc6bb19cf10087cf8182e8f3189ba425995beca 100644 (file)
@@ -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;
+       }
+}
index 468f1e9a1484c0a8e7c20213850990955732cd43..b20f6ead7b2d92826c43434ab692f67b9e242a19 100644 (file)
@@ -11,6 +11,7 @@
 
 #include "../perf.h"
 #include <linux/list.h>
+#include <linux/refcount.h>
 
 struct namespaces_event;
 
@@ -23,4 +24,36 @@ struct namespaces {
 struct namespaces *namespaces__new(struct namespaces_event *event);
 void namespaces__free(struct namespaces *namespaces);
 
+struct nsinfo {
+       pid_t                   pid;
+       bool                    need_setns;
+       char                    *mntns_path;
+       refcount_t              refcnt;
+};
+
+struct nscookie {
+       int                     oldns;
+       int                     newns;
+};
+
+void nsinfo__init(struct nsinfo *nsi);
+struct nsinfo *nsinfo__new(pid_t pid);
+void nsinfo__delete(struct nsinfo *nsi);
+
+struct nsinfo *nsinfo__get(struct nsinfo *nsi);
+void nsinfo__put(struct nsinfo *nsi);
+
+void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc);
+void nsinfo__mountns_exit(struct nscookie *nc);
+
+static inline void __nsinfo__zput(struct nsinfo **nsip)
+{
+       if (nsip) {
+               nsinfo__put(*nsip);
+               *nsip = NULL;
+       }
+}
+
+#define nsinfo__zput(nsi) __nsinfo__zput(&nsi)
+
 #endif  /* __PERF_NAMESPACES_H */
index e7a98dbd2aed9133d99f38b71b3cac9fb297e0d6..60a9eaa372ef724d70f3aa250d9f2d754093d257 100644 (file)
@@ -18,6 +18,8 @@
 #include "symbol.h"
 #include "strlist.h"
 #include "intlist.h"
+#include "namespaces.h"
+#include "vdso.h"
 #include "header.h"
 #include "path.h"
 #include "sane_ctype.h"
@@ -1436,9 +1438,17 @@ int dso__load(struct dso *dso, struct map *map)
        struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
        bool kmod;
        unsigned char build_id[BUILD_ID_SIZE];
+       struct nscookie nsc;
 
+       nsinfo__mountns_enter(dso->nsinfo, &nsc);
        pthread_mutex_lock(&dso->lock);
 
+       /* The vdso files always live in the host container, so don't go looking
+        * for them in the container's mount namespace.
+        */
+       if (dso__is_vdso(dso))
+               nsinfo__mountns_exit(&nsc);
+
        /* check again under the dso->lock */
        if (dso__loaded(dso, map->type)) {
                ret = 1;
@@ -1584,6 +1594,7 @@ out_free:
 out:
        dso__set_loaded(dso, map->type);
        pthread_mutex_unlock(&dso->lock);
+       nsinfo__mountns_exit(&nsc);
 
        return ret;
 }
index 378c418ca0c173ddcc83425d590c32d3d71d2a39..aee9a42102bac264e89e3e698431975006c9681c 100644 (file)
@@ -59,6 +59,8 @@ struct thread *thread__new(pid_t pid, pid_t tid)
                list_add(&comm->list, &thread->comm_list);
                refcount_set(&thread->refcnt, 1);
                RB_CLEAR_NODE(&thread->rb_node);
+               /* Thread holds first ref to nsdata. */
+               thread->nsinfo = nsinfo__new(pid);
        }
 
        return thread;
@@ -91,6 +93,7 @@ void thread__delete(struct thread *thread)
                comm__free(comm);
        }
        unwind__finish_access(thread);
+       nsinfo__zput(thread->nsinfo);
 
        free(thread);
 }
index 4eb849e9098f5ca0115cb90527dd9f564476e4ea..cb1a5dd5c2b9b61db889538ba9f60eed158082e4 100644 (file)
@@ -34,6 +34,7 @@ struct thread {
 
        void                    *priv;
        struct thread_stack     *ts;
+       struct nsinfo           *nsinfo;
 #ifdef HAVE_LIBUNWIND_SUPPORT
        void                            *addr_space;
        struct unwind_libunwind_ops     *unwind_libunwind_ops;