All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH tip/perf/core 0/7] namespace tracing improvements
@ 2017-07-01  2:18 Krister Johansen
  2017-07-01  2:18 ` [PATCH tip/perf/core 1/7] perf symbols: find symbols in different mount namespace Krister Johansen
                   ` (6 more replies)
  0 siblings, 7 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-01  2:18 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo
  Cc: Alexander Shishkin, linux-kernel, Krister Johansen

This patch set is similar to a set of features I implemented for bcc
back in April.  At a high-level it does the following:

- Allow the tracing tools to resolve symbols if the traced process has a
  binary that is in a different mount namespace from perf.

- Allow the perf-<pid>.map files to live either in /tmp in the target
  process' mount namespace, or the tracing process's /tmp in its own
  mount namespace.

- Adds the ability to set and record against uprobes and USDT probes
  when the target process resides in a different mount namespace.

With these changes I can get pretty good insight into what containerized
processes on my systems are doing.  In addition to the above, I also
modified the builid cache code to cache relevant bits from the target
namespaces so that it's possible to preserve more information should a
container exit and take the mounted volumes along with it.

-K

Krister Johansen (7):
  perf symbols: find symbols in different mount namespace
  perf maps: lookup maps in both intitial mountns and inner mountns.
  perf probe: allow placing uprobes in alternate namespaces.
  perf buildid-cache: support binary objects from other namespaces
  perf top: support lookup of symbols in other mount namespaces.
  perf documentation: updates for target-ns.
  perf buildid-cache: cache debuginfo

 tools/perf/Documentation/perf-buildid-cache.txt |   5 +
 tools/perf/Documentation/perf-probe.txt         |   5 +
 tools/perf/Documentation/perf-top.txt           |   4 +
 tools/perf/builtin-buildid-cache.c              |  54 +++++--
 tools/perf/builtin-probe.c                      |  46 +++++-
 tools/perf/builtin-top.c                        |  15 ++
 tools/perf/tests/sdt.c                          |   4 +-
 tools/perf/util/annotate.c                      |   2 +-
 tools/perf/util/build-id.c                      | 120 +++++++++++---
 tools/perf/util/build-id.h                      |  12 +-
 tools/perf/util/dso.c                           |  21 ++-
 tools/perf/util/dso.h                           |   3 +
 tools/perf/util/machine.c                       |  22 ++-
 tools/perf/util/map.c                           |  29 +++-
 tools/perf/util/map.h                           |   8 +-
 tools/perf/util/namespaces.c                    | 198 ++++++++++++++++++++++++
 tools/perf/util/namespaces.h                    |  36 +++++
 tools/perf/util/parse-events.c                  |   2 +-
 tools/perf/util/probe-event.c                   |  85 ++++++----
 tools/perf/util/probe-event.h                   |  10 +-
 tools/perf/util/probe-file.c                    |  19 ++-
 tools/perf/util/probe-file.h                    |   4 +-
 tools/perf/util/symbol.c                        |  93 +++++++++--
 tools/perf/util/thread.c                        |   3 +
 tools/perf/util/thread.h                        |   1 +
 tools/perf/util/util.c                          |  34 +++-
 tools/perf/util/util.h                          |   2 +
 27 files changed, 706 insertions(+), 131 deletions(-)

-- 
2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH tip/perf/core 1/7] perf symbols: find symbols in different mount namespace
  2017-07-01  2:18 [PATCH tip/perf/core 0/7] namespace tracing improvements Krister Johansen
@ 2017-07-01  2:18 ` Krister Johansen
  2017-07-03 18:38   ` Arnaldo Carvalho de Melo
  2017-07-01  2:18 ` [PATCH tip/perf/core 2/7] perf maps: lookup maps in both intitial mountns and inner mountns Krister Johansen
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-01  2:18 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo
  Cc: Alexander Shishkin, linux-kernel, Krister Johansen

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>
---
 tools/perf/util/dso.c        |   1 +
 tools/perf/util/dso.h        |   2 +
 tools/perf/util/map.c        |   2 +
 tools/perf/util/namespaces.c | 127 +++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/namespaces.h |  33 +++++++++++
 tools/perf/util/symbol.c     |  11 ++++
 tools/perf/util/thread.c     |   3 +
 tools/perf/util/thread.h     |   1 +
 8 files changed, 180 insertions(+)

diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 4e7ab61..beda40e 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -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);
 }
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index bd061ba..78ec637 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -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];
 };
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 2179b2d..5dc60ca 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -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;
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index 67dcbcc..d7f31e0 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)
+{
+	free(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;
+
+	(void) setns(nc->oldns, CLONE_NEWNS);
+
+	if (nc->oldns > -1) {
+		(void) close(nc->oldns);
+		nc->oldns = -1;
+	}
+
+	if (nc->newns > -1) {
+		(void) close(nc->newns);
+		nc->newns = -1;
+	}
+}
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 468f1e9..b20f6ea 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -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 */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e7a98db..60a9eaa 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -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 @@ int dso__load(struct dso *dso, struct map *map)
 out:
 	dso__set_loaded(dso, map->type);
 	pthread_mutex_unlock(&dso->lock);
+	nsinfo__mountns_exit(&nsc);
 
 	return ret;
 }
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 378c418..aee9a42 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -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);
 }
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 4eb849e..cb1a5dd 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -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;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH tip/perf/core 2/7] perf maps: lookup maps in both intitial mountns and inner mountns.
  2017-07-01  2:18 [PATCH tip/perf/core 0/7] namespace tracing improvements Krister Johansen
  2017-07-01  2:18 ` [PATCH tip/perf/core 1/7] perf symbols: find symbols in different mount namespace Krister Johansen
@ 2017-07-01  2:18 ` Krister Johansen
  2017-07-03 18:44   ` Arnaldo Carvalho de Melo
  2017-07-01  2:18 ` [PATCH tip/perf/core 3/7] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-01  2:18 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo
  Cc: Alexander Shishkin, linux-kernel, Krister Johansen

If a process is in a mountns and has symbols in /tmp/perf-<pid>.map,
look first in the namespace using the tgid for the pidns that the
process might be in.  If the map isn't found there, try looking in
the mountns where perf is running, and use the tgid that's appropriate
for perf's pid namespace.  If all else fails, use the original pid.

This allows us to locate a symbol map file in the mount namespace, if
it was generated there.  However, we also try the tool's /tmp in case
it's there instead.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/util/machine.c    | 19 ++++------
 tools/perf/util/map.c        | 29 ++++++++++++----
 tools/perf/util/map.h        |  8 ++---
 tools/perf/util/namespaces.c | 83 ++++++++++++++++++++++++++++++++++++++++----
 tools/perf/util/namespaces.h |  5 ++-
 tools/perf/util/symbol.c     | 71 ++++++++++++++++++++++++++++++-------
 6 files changed, 172 insertions(+), 43 deletions(-)

diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index d7f31cb..ed98881 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1388,13 +1388,10 @@ int machine__process_mmap2_event(struct machine *machine,
 	else
 		type = MAP__FUNCTION;
 
-	map = map__new(machine, event->mmap2.start,
-			event->mmap2.len, event->mmap2.pgoff,
-			event->mmap2.pid, event->mmap2.maj,
-			event->mmap2.min, event->mmap2.ino,
-			event->mmap2.ino_generation,
-			event->mmap2.prot,
-			event->mmap2.flags,
+	map = map__new(machine, event->mmap2.start, event->mmap2.len,
+			event->mmap2.pgoff, event->mmap2.maj, event->mmap2.min,
+			event->mmap2.ino, event->mmap2.ino_generation,
+			event->mmap2.prot, event->mmap2.flags,
 			event->mmap2.filename, type, thread);
 
 	if (map == NULL)
@@ -1446,11 +1443,9 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
 	else
 		type = MAP__FUNCTION;
 
-	map = map__new(machine, event->mmap.start,
-			event->mmap.len, event->mmap.pgoff,
-			event->mmap.pid, 0, 0, 0, 0, 0, 0,
-			event->mmap.filename,
-			type, thread);
+	map = map__new(machine, event->mmap.start, event->mmap.len,
+			event->mmap.pgoff, 0, 0, 0, 0, 0, 0,
+			event->mmap.filename, type, thread);
 
 	if (map == NULL)
 		goto out_problem_map;
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 5dc60ca..2bd20cb 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -145,12 +145,14 @@ void map__init(struct map *map, enum map_type type,
 	refcount_set(&map->refcnt, 1);
 }
 
-struct map *map__new(struct machine *machine, u64 start, u64 len,
-		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
-		     u64 ino_gen, u32 prot, u32 flags, char *filename,
-		     enum map_type type, struct thread *thread)
+struct map *map__new(struct machine *machine, u64 start, u64 len, u64 pgoff,
+		     u32 d_maj, u32 d_min, u64 ino, u64 ino_gen, u32 prot,
+		     u32 flags, char *filename, enum map_type type,
+		     struct thread *thread)
 {
 	struct map *map = malloc(sizeof(*map));
+	struct nsinfo *nsi = NULL;
+	struct nsinfo *nnsi;
 
 	if (map != NULL) {
 		char newfilename[PATH_MAX];
@@ -168,9 +170,11 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
 		map->ino_generation = ino_gen;
 		map->prot = prot;
 		map->flags = flags;
+		nsi = nsinfo__get(thread->nsinfo);
 
-		if ((anon || no_dso) && type == MAP__FUNCTION) {
-			snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
+		if ((anon || no_dso) && nsi && type == MAP__FUNCTION) {
+			snprintf(newfilename, sizeof(newfilename),
+				 "/tmp/perf-%d.map", nsi->pid);
 			filename = newfilename;
 		}
 
@@ -180,6 +184,16 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
 		}
 
 		if (vdso) {
+			/* The vdso maps are always on the host and not the
+			 * container.  Ensure that we don't use setns to look
+			 * them up.
+			 */
+			nnsi = nsinfo__copy(nsi);
+			if (nnsi) {
+				nsinfo__put(nsi);
+				nnsi->need_setns = false;
+				nsi = nnsi;
+			}
 			pgoff = 0;
 			dso = machine__findnew_vdso(machine, thread);
 		} else
@@ -201,11 +215,12 @@ 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->nsinfo = nsi;
 		dso__put(dso);
 	}
 	return map;
 out_delete:
+	nsinfo__put(nsi);
 	free(map);
 	return NULL;
 }
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f9e8ac8..bf015a93 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -140,10 +140,10 @@ struct thread;
 
 void map__init(struct map *map, enum map_type type,
 	       u64 start, u64 end, u64 pgoff, struct dso *dso);
-struct map *map__new(struct machine *machine, u64 start, u64 len,
-		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
-		     u64 ino_gen, u32 prot, u32 flags,
-		     char *filename, enum map_type type, struct thread *thread);
+struct map *map__new(struct machine *machine, u64 start, u64 len, u64 pgoff,
+		     u32 d_maj, u32 d_min, u64 ino, u64 ino_gen, u32 prot,
+		     u32 flags, char *filename, enum map_type type,
+		     struct thread *thread);
 struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
 void map__delete(struct map *map);
 struct map *map__clone(struct map *map);
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index d7f31e0..cad7d8b 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -40,18 +40,23 @@ void namespaces__free(struct namespaces *namespaces)
 	free(namespaces);
 }
 
-void nsinfo__init(struct nsinfo *nsi)
+int nsinfo__init(struct nsinfo *nsi)
 {
 	char oldns[PATH_MAX];
+	char spath[PATH_MAX];
 	char *newns = NULL;
+	char *statln = NULL;
 	struct stat old_stat;
 	struct stat new_stat;
+	FILE *f = NULL;
+	size_t linesz = 0;
+	int rv = -1;
 
 	if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
-		return;
+		return rv;
 
 	if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1)
-		return;
+		return rv;
 
 	if (stat(oldns, &old_stat) < 0)
 		goto out;
@@ -68,24 +73,89 @@ void nsinfo__init(struct nsinfo *nsi)
 		newns = NULL;
 	}
 
+	/* If we're dealing with a process that is in a different PID namespace,
+	 * attempt to work out the innermost tgid for the process.
+	 */
+	if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsi->pid) >= PATH_MAX)
+		goto out;
+
+	f = fopen(spath, "r");
+	if (f == NULL)
+		goto out;
+
+	while (getline(&statln, &linesz, f) != -1) {
+		/* Use tgid if CONFIG_PID_NS is not defined. */
+		if (strstr(statln, "Tgid:") != NULL) {
+			nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'),
+						     NULL, 10);
+			nsi->nstgid = nsi->tgid;
+		}
+
+		if (strstr(statln, "NStgid:") != NULL) {
+			nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'),
+						     NULL, 10);
+			break;
+		}
+	}
+	rv = 0;
+
 out:
+	if (f != NULL)
+		(void) fclose(f);
+	free(statln);
 	free(newns);
+	return rv;
 }
 
 struct nsinfo *nsinfo__new(pid_t pid)
 {
-	struct nsinfo *nsi = calloc(1, sizeof(*nsi));
+	struct nsinfo *nsi;
 
+	if (pid == 0)
+		return NULL;
+
+	nsi = calloc(1, sizeof(*nsi));
 	if (nsi != NULL) {
 		nsi->pid = pid;
+		nsi->tgid = pid;
+		nsi->nstgid = pid;
 		nsi->need_setns = false;
-		nsinfo__init(nsi);
+		/* Init may fail if the process exits while we're trying to look
+		 * at its proc information.  In that case, save the pid but
+		 * don't try to enter the namespace.
+		 */
+		if (nsinfo__init(nsi) == -1)
+			nsi->need_setns = false;
+
 		refcount_set(&nsi->refcnt, 1);
 	}
 
 	return nsi;
 }
 
+struct nsinfo *nsinfo__copy(struct nsinfo *nsi)
+{
+	struct nsinfo *nnsi;
+
+	nnsi = calloc(1, sizeof(*nnsi));
+	if (nnsi != NULL) {
+		nnsi->pid = nsi->pid;
+		nnsi->tgid = nsi->tgid;
+		nnsi->nstgid = nsi->nstgid;
+		nnsi->need_setns = nsi->need_setns;
+		if (nsi->mntns_path) {
+			nnsi->mntns_path = strdup(nsi->mntns_path);
+			if (!nnsi->mntns_path) {
+				free(nnsi);
+				return NULL;
+			}
+		}
+		refcount_set(&nnsi->refcnt, 1);
+	}
+
+	return nnsi;
+}
+
 void nsinfo__delete(struct nsinfo *nsi)
 {
 	free(nsi->mntns_path);
@@ -105,7 +175,8 @@ void nsinfo__put(struct nsinfo *nsi)
 		nsinfo__delete(nsi);
 }
 
-void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc)
+void nsinfo__mountns_enter(struct nsinfo *nsi,
+				  struct nscookie *nc)
 {
 	char curpath[PATH_MAX];
 	int oldns = -1;
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index b20f6ea..f19aa41 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -26,6 +26,8 @@ void namespaces__free(struct namespaces *namespaces);
 
 struct nsinfo {
 	pid_t			pid;
+	pid_t			tgid;
+	pid_t			nstgid;
 	bool			need_setns;
 	char			*mntns_path;
 	refcount_t		refcnt;
@@ -36,8 +38,9 @@ struct nscookie {
 	int			newns;
 };
 
-void nsinfo__init(struct nsinfo *nsi);
+int nsinfo__init(struct nsinfo *nsi);
 struct nsinfo *nsinfo__new(pid_t pid);
+struct nsinfo *nsinfo__copy(struct nsinfo *nsi);
 void nsinfo__delete(struct nsinfo *nsi);
 
 struct nsinfo *nsinfo__get(struct nsinfo *nsi);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 60a9eaa..97e454f 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -19,7 +19,6 @@
 #include "strlist.h"
 #include "intlist.h"
 #include "namespaces.h"
-#include "vdso.h"
 #include "header.h"
 #include "path.h"
 #include "sane_ctype.h"
@@ -1327,14 +1326,15 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
 	return __dso__load_kallsyms(dso, filename, map, false);
 }
 
-static int dso__load_perf_map(struct dso *dso, struct map *map)
+static int dso__load_perf_map(const char *map_path, struct dso *dso,
+			      struct map *map)
 {
 	char *line = NULL;
 	size_t n;
 	FILE *file;
 	int nr_syms = 0;
 
-	file = fopen(dso->long_name, "r");
+	file = fopen(map_path, "r");
 	if (file == NULL)
 		goto out_failure;
 
@@ -1426,6 +1426,45 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
 	}
 }
 
+/* Checks for the existence of the perf-<pid>.map file in two different
+ * locations.  First, if the process is a separate mount namespace, check in
+ * that namespace using the pid of the innermost pid namespace.  If's not in a
+ * namespace, or the file can't be found there, try in the mount namespace of
+ * the tracing process using our view of its pid.
+ */
+static int dso__find_perf_map(char *filebuf, size_t bufsz,
+			      struct nsinfo **nsip)
+{
+	struct nscookie nsc;
+	struct nsinfo *nsi;
+	struct nsinfo *nnsi;
+	int rc = -1;
+
+	nsi = *nsip;
+
+	if (nsi->need_setns) {
+		snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nsi->nstgid);
+		nsinfo__mountns_enter(nsi, &nsc);
+		rc = access(filebuf, R_OK);
+		nsinfo__mountns_exit(&nsc);
+		if (rc == 0)
+			return rc;
+	}
+
+	nnsi = nsinfo__copy(nsi);
+	if (nnsi) {
+		nsinfo__put(nsi);
+
+		nnsi->need_setns = false;
+		snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nnsi->tgid);
+		*nsip = nnsi;
+		rc = 0;
+	}
+
+	return rc;
+}
+
+
 int dso__load(struct dso *dso, struct map *map)
 {
 	char *name;
@@ -1437,18 +1476,23 @@ int dso__load(struct dso *dso, struct map *map)
 	struct symsrc ss_[2];
 	struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
 	bool kmod;
+	bool perfmap;
 	unsigned char build_id[BUILD_ID_SIZE];
 	struct nscookie nsc;
+	char newmapname[PATH_MAX];
+	const char *map_path = dso->long_name;
+
+	perfmap = strncmp(dso->name, "/tmp/perf-", 10) == 0;
+	if (perfmap) {
+		if (dso->nsinfo && (dso__find_perf_map(newmapname,
+		    sizeof(newmapname), &dso->nsinfo) == 0)) {
+			map_path = newmapname;
+		}
+	}
 
 	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;
@@ -1471,19 +1515,20 @@ int dso__load(struct dso *dso, struct map *map)
 
 	dso->adjust_symbols = 0;
 
-	if (strncmp(dso->name, "/tmp/perf-", 10) == 0) {
+	if (perfmap) {
 		struct stat st;
 
-		if (lstat(dso->name, &st) < 0)
+		if (lstat(map_path, &st) < 0)
 			goto out;
 
 		if (!symbol_conf.force && st.st_uid && (st.st_uid != geteuid())) {
 			pr_warning("File %s not owned by current user or root, "
-				   "ignoring it (use -f to override).\n", dso->name);
+				   "ignoring it (use -f to override).\n",
+				   map_path);
 			goto out;
 		}
 
-		ret = dso__load_perf_map(dso, map);
+		ret = dso__load_perf_map(map_path, dso, map);
 		dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT :
 					     DSO_BINARY_TYPE__NOT_FOUND;
 		goto out;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH tip/perf/core 3/7] perf probe: allow placing uprobes in alternate namespaces.
  2017-07-01  2:18 [PATCH tip/perf/core 0/7] namespace tracing improvements Krister Johansen
  2017-07-01  2:18 ` [PATCH tip/perf/core 1/7] perf symbols: find symbols in different mount namespace Krister Johansen
  2017-07-01  2:18 ` [PATCH tip/perf/core 2/7] perf maps: lookup maps in both intitial mountns and inner mountns Krister Johansen
@ 2017-07-01  2:18 ` Krister Johansen
  2017-07-03 18:46   ` Arnaldo Carvalho de Melo
  2017-07-01  2:18 ` [PATCH tip/perf/core 4/7] perf buildid-cache: support binary objects from other namespaces Krister Johansen
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-01  2:18 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo
  Cc: Alexander Shishkin, linux-kernel, Krister Johansen

Teaches perf how to place a uprobe on a file that's in a different mount
namespace.  The user must add the probe using the --target-ns argument
to perf probe.  Once it has been placed, it may be recorded against
without further namespace-specific commands.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/builtin-probe.c    | 44 ++++++++++++++++++++++--
 tools/perf/util/probe-event.c | 79 +++++++++++++++++++++++++++++--------------
 tools/perf/util/probe-event.h | 10 ++++--
 3 files changed, 101 insertions(+), 32 deletions(-)

diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index cf9f9e9..5ab2e00 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -58,6 +58,7 @@ static struct {
 	struct line_range line_range;
 	char *target;
 	struct strfilter *filter;
+	struct nsinfo *nsi;
 } params;
 
 /* Parse an event definition. Note that any error must die. */
@@ -80,6 +81,9 @@ static int parse_probe_event(const char *str)
 		params.target_used = true;
 	}
 
+	if (params.nsi)
+		pev->nsi = nsinfo__get(params.nsi);
+
 	/* Parse a perf-probe command into event */
 	ret = parse_perf_probe_command(str, pev);
 	pr_debug("%d arguments\n", pev->nargs);
@@ -178,6 +182,7 @@ static int opt_set_target(const struct option *opt, const char *str,
 {
 	int ret = -ENOENT;
 	char *tmp;
+	struct nscookie nsc;
 
 	if  (str) {
 		if (!strcmp(opt->long_name, "exec"))
@@ -189,7 +194,9 @@ static int opt_set_target(const struct option *opt, const char *str,
 
 		/* Expand given path to absolute path, except for modulename */
 		if (params.uprobes || strchr(str, '/')) {
+			nsinfo__mountns_enter(params.nsi, &nsc);
 			tmp = realpath(str, NULL);
+			nsinfo__mountns_exit(&nsc);
 			if (!tmp) {
 				pr_warning("Failed to get the absolute path of %s: %m\n", str);
 				return ret;
@@ -208,6 +215,34 @@ static int opt_set_target(const struct option *opt, const char *str,
 	return ret;
 }
 
+static int opt_set_target_ns(const struct option *opt __maybe_unused,
+			     const char *str, int unset __maybe_unused)
+{
+	int ret = -ENOENT;
+	pid_t ns_pid;
+	struct nsinfo *nsip;
+
+	if (str) {
+		errno = 0;
+		ns_pid = (pid_t)strtol(str, NULL, 10);
+		if (errno != 0) {
+			ret = -errno;
+			pr_warning("Failed to parse %s as a pid: %s\n", str,
+				   strerror(errno));
+			return ret;
+		}
+		nsip = nsinfo__new(ns_pid);
+		if (nsip && nsip->need_setns)
+			params.nsi = nsinfo__get(nsip);
+		nsinfo__put(nsip);
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+
 /* Command option callbacks */
 
 #ifdef HAVE_DWARF_SUPPORT
@@ -299,6 +334,7 @@ static void cleanup_params(void)
 	line_range__clear(&params.line_range);
 	free(params.target);
 	strfilter__delete(params.filter);
+	nsinfo__put(params.nsi);
 	memset(&params, 0, sizeof(params));
 }
 
@@ -554,6 +590,8 @@ __cmd_probe(int argc, const char **argv)
 	OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
 	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
 		   "Look for files with symbols relative to this directory"),
+	OPT_CALLBACK(0, "target-ns", NULL, "pid",
+		     "target pid for namespace information", opt_set_target_ns),
 	OPT_END()
 	};
 	int ret;
@@ -634,15 +672,15 @@ __cmd_probe(int argc, const char **argv)
 			pr_err_with_code("  Error: Failed to show event list.", ret);
 		return ret;
 	case 'F':
-		ret = show_available_funcs(params.target, params.filter,
-					params.uprobes);
+		ret = show_available_funcs(params.target, params.nsi,
+					   params.filter, params.uprobes);
 		if (ret < 0)
 			pr_err_with_code("  Error: Failed to show functions.", ret);
 		return ret;
 #ifdef HAVE_DWARF_SUPPORT
 	case 'L':
 		ret = show_line_range(&params.line_range, params.target,
-				      params.uprobes);
+				      params.nsi, params.uprobes);
 		if (ret < 0)
 			pr_err_with_code("  Error: Failed to show lines.", ret);
 		return ret;
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 84e7e69..dce4f12 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -184,13 +184,19 @@ static struct map *kernel_get_module_map(const char *module)
 	return NULL;
 }
 
-struct map *get_target_map(const char *target, bool user)
+struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user)
 {
 	/* Init maps of given executable or kernel */
-	if (user)
-		return dso__new_map(target);
-	else
+	if (user) {
+		struct map *map;
+
+		map = dso__new_map(target);
+		if (map && map->dso)
+			map->dso->nsinfo = nsinfo__get(nsi);
+		return map;
+	} else {
 		return kernel_get_module_map(target);
+	}
 }
 
 static int convert_exec_to_group(const char *exec, char **result)
@@ -366,7 +372,8 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
 static int find_alternative_probe_point(struct debuginfo *dinfo,
 					struct perf_probe_point *pp,
 					struct perf_probe_point *result,
-					const char *target, bool uprobes)
+					const char *target, struct nsinfo *nsi,
+					bool uprobes)
 {
 	struct map *map = NULL;
 	struct symbol *sym;
@@ -377,7 +384,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
 	if (!pp->function || pp->file)
 		return -ENOTSUP;
 
-	map = get_target_map(target, uprobes);
+	map = get_target_map(target, nsi, uprobes);
 	if (!map)
 		return -EINVAL;
 
@@ -421,8 +428,8 @@ static int get_alternative_probe_event(struct debuginfo *dinfo,
 
 	memcpy(tmp, &pev->point, sizeof(*tmp));
 	memset(&pev->point, 0, sizeof(pev->point));
-	ret = find_alternative_probe_point(dinfo, tmp, &pev->point,
-					   pev->target, pev->uprobes);
+	ret = find_alternative_probe_point(dinfo, tmp, &pev->point, pev->target,
+					   pev->nsi, pev->uprobes);
 	if (ret < 0)
 		memcpy(&pev->point, tmp, sizeof(*tmp));
 
@@ -444,7 +451,7 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
 	if (lr->end != INT_MAX)
 		len = lr->end - lr->start;
 	ret = find_alternative_probe_point(dinfo, &pp, &result,
-					   target, user);
+					   target, NULL, user);
 	if (!ret) {
 		lr->function = result.function;
 		lr->file = result.file;
@@ -457,12 +464,14 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
 }
 
 /* Open new debuginfo of given module */
-static struct debuginfo *open_debuginfo(const char *module, bool silent)
+static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
+					bool silent)
 {
 	const char *path = module;
 	char reason[STRERR_BUFSIZE];
 	struct debuginfo *ret = NULL;
 	struct dso *dso = NULL;
+	struct nscookie nsc;
 	int err;
 
 	if (!module || !strchr(module, '/')) {
@@ -480,6 +489,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
 		}
 		path = dso->long_name;
 	}
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = debuginfo__new(path);
 	if (!ret && !silent) {
 		pr_warning("The %s file has no debug information.\n", path);
@@ -489,6 +499,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
 			pr_warning("Rebuild with -g, ");
 		pr_warning("or install an appropriate debuginfo package.\n");
 	}
+	nsinfo__mountns_exit(&nsc);
 	return ret;
 }
 
@@ -516,7 +527,7 @@ static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
 		goto out;
 	}
 
-	debuginfo_cache = open_debuginfo(module, silent);
+	debuginfo_cache = open_debuginfo(module, NULL, silent);
 	if (!debuginfo_cache)
 		zfree(&debuginfo_cache_path);
 out:
@@ -531,14 +542,18 @@ static void debuginfo_cache__exit(void)
 }
 
 
-static int get_text_start_address(const char *exec, unsigned long *address)
+static int get_text_start_address(const char *exec, unsigned long *address,
+				  struct nsinfo *nsi)
 {
 	Elf *elf;
 	GElf_Ehdr ehdr;
 	GElf_Shdr shdr;
 	int fd, ret = -ENOENT;
+	struct nscookie nsc;
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	fd = open(exec, O_RDONLY);
+	nsinfo__mountns_exit(&nsc);
 	if (fd < 0)
 		return -errno;
 
@@ -582,7 +597,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
 			ret = -EINVAL;
 			goto error;
 		}
-		ret = get_text_start_address(tp->module, &stext);
+		ret = get_text_start_address(tp->module, &stext, NULL);
 		if (ret < 0)
 			goto error;
 		addr += stext;
@@ -659,7 +674,7 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
 
 	/* Prepare a map for offline binary */
 	map = dso__new_map(pathname);
-	if (!map || get_text_start_address(pathname, &stext) < 0) {
+	if (!map || get_text_start_address(pathname, &stext, NULL) < 0) {
 		pr_warning("Failed to get ELF symbols for %s\n", pathname);
 		return -EINVAL;
 	}
@@ -676,7 +691,8 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
 }
 
 static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
-					  int ntevs, const char *exec)
+					  int ntevs, const char *exec,
+					  struct nsinfo *nsi)
 {
 	int i, ret = 0;
 	unsigned long stext = 0;
@@ -684,7 +700,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
 	if (!exec)
 		return 0;
 
-	ret = get_text_start_address(exec, &stext);
+	ret = get_text_start_address(exec, &stext, nsi);
 	if (ret < 0)
 		return ret;
 
@@ -715,7 +731,7 @@ post_process_module_probe_trace_events(struct probe_trace_event *tevs,
 	if (!module)
 		return 0;
 
-	map = get_target_map(module, false);
+	map = get_target_map(module, NULL, false);
 	if (!map || debuginfo__get_text_offset(dinfo, &text_offs, true) < 0) {
 		pr_warning("Failed to get ELF symbols for %s\n", module);
 		return -EINVAL;
@@ -802,7 +818,8 @@ static int post_process_probe_trace_events(struct perf_probe_event *pev,
 	int ret;
 
 	if (uprobe)
-		ret = add_exec_to_probe_trace_events(tevs, ntevs, module);
+		ret = add_exec_to_probe_trace_events(tevs, ntevs, module,
+						     pev->nsi);
 	else if (module)
 		/* Currently ref_reloc_sym based probe is not for drivers */
 		ret = post_process_module_probe_trace_events(tevs, ntevs,
@@ -825,7 +842,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 	struct debuginfo *dinfo;
 	int ntevs, ret = 0;
 
-	dinfo = open_debuginfo(pev->target, !need_dwarf);
+	dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf);
 	if (!dinfo) {
 		if (need_dwarf)
 			return -ENOENT;
@@ -945,7 +962,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
 	char sbuf[STRERR_BUFSIZE];
 
 	/* Search a line range */
-	dinfo = open_debuginfo(module, false);
+	dinfo = open_debuginfo(module, NULL, false);
 	if (!dinfo)
 		return -ENOENT;
 
@@ -1021,14 +1038,18 @@ static int __show_line_range(struct line_range *lr, const char *module,
 	return ret;
 }
 
-int show_line_range(struct line_range *lr, const char *module, bool user)
+int show_line_range(struct line_range *lr, const char *module,
+		    struct nsinfo *nsi, bool user)
 {
 	int ret;
+	struct nscookie nsc;
 
 	ret = init_probe_symbol_maps(user);
 	if (ret < 0)
 		return ret;
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = __show_line_range(lr, module, user);
+	nsinfo__mountns_exit(&nsc);
 	exit_probe_symbol_maps();
 
 	return ret;
@@ -1111,7 +1132,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
 	if (ret < 0)
 		return ret;
 
-	dinfo = open_debuginfo(pevs->target, false);
+	dinfo = open_debuginfo(pevs->target, pevs->nsi, false);
 	if (!dinfo) {
 		ret = -ENOENT;
 		goto out;
@@ -2703,6 +2724,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	struct probe_trace_event *tev = NULL;
 	struct probe_cache *cache = NULL;
 	struct strlist *namelist[2] = {NULL, NULL};
+	struct nscookie nsc;
 
 	up = pev->uprobes ? 1 : 0;
 	fd[up] = __open_probe_file_and_namelist(up, &namelist[up]);
@@ -2729,7 +2751,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 		if (ret < 0)
 			break;
 
+		nsinfo__mountns_enter(pev->nsi, &nsc);
 		ret = probe_file__add_event(fd[up], tev);
+		nsinfo__mountns_exit(&nsc);
 		if (ret < 0)
 			break;
 
@@ -2805,7 +2829,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
 	int ret, i, j, skipped = 0;
 	char *mod_name;
 
-	map = get_target_map(pev->target, pev->uprobes);
+	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
 	if (!map) {
 		ret = -EINVAL;
 		goto out;
@@ -3345,13 +3369,16 @@ int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 {
 	int i, j;
+	struct perf_probe_event *pev;
 
 	/* Loop 3: cleanup and free trace events  */
 	for (i = 0; i < npevs; i++) {
+		pev = &pevs[i];
 		for (j = 0; j < pevs[i].ntevs; j++)
 			clear_probe_trace_event(&pevs[i].tevs[j]);
 		zfree(&pevs[i].tevs);
 		pevs[i].ntevs = 0;
+		nsinfo__zput(pev->nsi);
 		clear_perf_probe_event(&pevs[i]);
 	}
 }
@@ -3409,8 +3436,8 @@ int del_perf_probe_events(struct strfilter *filter)
 	return ret;
 }
 
-int show_available_funcs(const char *target, struct strfilter *_filter,
-					bool user)
+int show_available_funcs(const char *target, struct nsinfo *nsi,
+			 struct strfilter *_filter, bool user)
 {
         struct rb_node *nd;
 	struct map *map;
@@ -3421,7 +3448,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
 		return ret;
 
 	/* Get a symbol map */
-	map = get_target_map(target, user);
+	map = get_target_map(target, nsi, user);
 	if (!map) {
 		pr_err("Failed to get a map for %s\n", (target) ? : "kernel");
 		return -EINVAL;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5812947..078681d 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -4,6 +4,7 @@
 #include <linux/compiler.h>
 #include <stdbool.h>
 #include "intlist.h"
+#include "namespaces.h"
 
 /* Probe related configurations */
 struct probe_conf {
@@ -92,6 +93,7 @@ struct perf_probe_event {
 	struct perf_probe_arg	*args;	/* Arguments */
 	struct probe_trace_event *tevs;
 	int			ntevs;
+	struct nsinfo		*nsi;	/* Target namespace */
 };
 
 /* Line range */
@@ -163,10 +165,12 @@ int show_perf_probe_event(const char *group, const char *event,
 			  struct perf_probe_event *pev,
 			  const char *module, bool use_stdout);
 int show_perf_probe_events(struct strfilter *filter);
-int show_line_range(struct line_range *lr, const char *module, bool user);
+int show_line_range(struct line_range *lr, const char *module,
+		    struct nsinfo *nsi, bool user);
 int show_available_vars(struct perf_probe_event *pevs, int npevs,
 			struct strfilter *filter);
-int show_available_funcs(const char *module, struct strfilter *filter, bool user);
+int show_available_funcs(const char *module, struct nsinfo *nsi,
+			 struct strfilter *filter, bool user);
 void arch__fix_tev_from_maps(struct perf_probe_event *pev,
 			     struct probe_trace_event *tev, struct map *map,
 			     struct symbol *sym);
@@ -180,7 +184,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...) __printf(3, 4);
 int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
 			    struct perf_probe_arg *pvar);
 
-struct map *get_target_map(const char *target, bool user);
+struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user);
 
 void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
 					   int ntevs);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH tip/perf/core 4/7] perf buildid-cache: support binary objects from other namespaces
  2017-07-01  2:18 [PATCH tip/perf/core 0/7] namespace tracing improvements Krister Johansen
                   ` (2 preceding siblings ...)
  2017-07-01  2:18 ` [PATCH tip/perf/core 3/7] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
@ 2017-07-01  2:18 ` Krister Johansen
  2017-07-01  2:18 ` [PATCH tip/perf/core 5/7] perf top: support lookup of symbols in other mount namespaces Krister Johansen
                   ` (2 subsequent siblings)
  6 siblings, 0 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-01  2:18 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo
  Cc: Alexander Shishkin, linux-kernel, Krister Johansen

Teach buildid-cache how to add, remove, and update binary objects from
other mount namespaces.  Allow probe events tracing binaries in
different namespaces to add their objects to the probe and build-id
caches too.  As a handy side effect, this also lets us access SDT probes
in binaries from alternate mount namespaces.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/builtin-buildid-cache.c | 52 +++++++++++++++++++++++++++-----------
 tools/perf/builtin-probe.c         |  2 +-
 tools/perf/tests/sdt.c             |  4 +--
 tools/perf/util/build-id.c         | 48 ++++++++++++++++++++++++-----------
 tools/perf/util/build-id.h         |  9 ++++---
 tools/perf/util/dso.c              | 12 ++++++++-
 tools/perf/util/parse-events.c     |  2 +-
 tools/perf/util/probe-event.c      |  6 ++---
 tools/perf/util/probe-file.c       | 19 +++++++++-----
 tools/perf/util/probe-file.h       |  4 +--
 tools/perf/util/symbol.c           | 19 ++++++++++----
 tools/perf/util/util.c             | 34 ++++++++++++++++++++-----
 tools/perf/util/util.h             |  2 ++
 13 files changed, 152 insertions(+), 61 deletions(-)

diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 9eba7f1..0b9a43e 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -14,6 +14,7 @@
 #include <unistd.h>
 #include "builtin.h"
 #include "perf.h"
+#include "namespaces.h"
 #include "util/cache.h"
 #include "util/debug.h"
 #include "util/header.h"
@@ -165,33 +166,41 @@ static int build_id_cache__add_kcore(const char *filename, bool force)
 	return 0;
 }
 
-static int build_id_cache__add_file(const char *filename)
+static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi)
 {
 	char sbuild_id[SBUILD_ID_SIZE];
 	u8 build_id[BUILD_ID_SIZE];
 	int err;
+	struct nscookie nsc;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
-	err = build_id_cache__add_s(sbuild_id, filename,
+	err = build_id_cache__add_s(sbuild_id, filename, nsi,
 				    false, false);
 	pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
 		 err ? "FAIL" : "Ok");
 	return err;
 }
 
-static int build_id_cache__remove_file(const char *filename)
+static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi)
 {
 	u8 build_id[BUILD_ID_SIZE];
 	char sbuild_id[SBUILD_ID_SIZE];
+	struct nscookie nsc;
 
 	int err;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
@@ -204,13 +213,13 @@ static int build_id_cache__remove_file(const char *filename)
 	return err;
 }
 
-static int build_id_cache__purge_path(const char *pathname)
+static int build_id_cache__purge_path(const char *pathname, struct nsinfo *nsi)
 {
 	struct strlist *list;
 	struct str_node *pos;
 	int err;
 
-	err = build_id_cache__list_build_ids(pathname, &list);
+	err = build_id_cache__list_build_ids(pathname, nsi, &list);
 	if (err)
 		goto out;
 
@@ -256,24 +265,30 @@ static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *f
 	return 0;
 }
 
-static int build_id_cache__update_file(const char *filename)
+static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi)
 {
 	u8 build_id[BUILD_ID_SIZE];
 	char sbuild_id[SBUILD_ID_SIZE];
+	struct nscookie nsc;
 
-	int err = 0;
+	int err;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
+	err = 0;
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
 	if (build_id_cache__cached(sbuild_id))
 		err = build_id_cache__remove_s(sbuild_id);
 
 	if (!err)
-		err = build_id_cache__add_s(sbuild_id, filename, false, false);
+		err = build_id_cache__add_s(sbuild_id, filename, nsi, false,
+					    false);
 
 	pr_debug("Updating %s %s: %s\n", sbuild_id, filename,
 		 err ? "FAIL" : "Ok");
@@ -286,6 +301,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 	struct strlist *list;
 	struct str_node *pos;
 	int ret = 0;
+	int ns_id = -1;
 	bool force = false;
 	char const *add_name_list_str = NULL,
 		   *remove_name_list_str = NULL,
@@ -299,6 +315,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		.mode  = PERF_DATA_MODE_READ,
 	};
 	struct perf_session *session = NULL;
+	struct nsinfo *nsi = NULL;
 
 	const struct option buildid_cache_options[] = {
 	OPT_STRING('a', "add", &add_name_list_str,
@@ -315,6 +332,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 	OPT_STRING('u', "update", &update_name_list_str, "file list",
 		    "file(s) to update"),
 	OPT_INCR('v', "verbose", &verbose, "be more verbose"),
+	OPT_INTEGER(0, "target-ns", &ns_id, "mount namespace of target pid"),
 	OPT_END()
 	};
 	const char * const buildid_cache_usage[] = {
@@ -330,6 +348,9 @@ int cmd_buildid_cache(int argc, const char **argv)
 		     !missing_filename && !update_name_list_str))
 		usage_with_options(buildid_cache_usage, buildid_cache_options);
 
+	if (ns_id > 0)
+		nsi = nsinfo__new(ns_id);
+
 	if (missing_filename) {
 		file.path = missing_filename;
 		file.force = force;
@@ -348,7 +369,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(add_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__add_file(pos->s)) {
+				if (build_id_cache__add_file(pos->s, nsi)) {
 					if (errno == EEXIST) {
 						pr_debug("%s already in the cache\n",
 							 pos->s);
@@ -366,7 +387,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(remove_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__remove_file(pos->s)) {
+				if (build_id_cache__remove_file(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -384,7 +405,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(purge_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__purge_path(pos->s)) {
+				if (build_id_cache__purge_path(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -405,7 +426,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(update_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__update_file(pos->s)) {
+				if (build_id_cache__update_file(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -424,6 +445,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 
 out:
 	perf_session__delete(session);
+	nsinfo__zput(nsi);
 
 	return ret;
 }
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 5ab2e00..808b5dd 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -419,7 +419,7 @@ static int del_perf_probe_caches(struct strfilter *filter)
 	}
 
 	strlist__for_each_entry(nd, bidlist) {
-		cache = probe_cache__new(nd->s);
+		cache = probe_cache__new(nd->s, NULL);
 		if (!cache)
 			continue;
 		if (probe_cache__filter_purge(cache, filter) < 0 ||
diff --git a/tools/perf/tests/sdt.c b/tools/perf/tests/sdt.c
index 06eda67..5abe8ce 100644
--- a/tools/perf/tests/sdt.c
+++ b/tools/perf/tests/sdt.c
@@ -33,7 +33,7 @@ static int build_id_cache__add_file(const char *filename)
 	}
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
-	err = build_id_cache__add_s(sbuild_id, filename, false, false);
+	err = build_id_cache__add_s(sbuild_id, filename, NULL, false, false);
 	if (err < 0)
 		pr_debug("Failed to add build id cache of %s\n", filename);
 	return err;
@@ -54,7 +54,7 @@ static char *get_self_path(void)
 static int search_cached_probe(const char *target,
 			       const char *group, const char *event)
 {
-	struct probe_cache *cache = probe_cache__new(target);
+	struct probe_cache *cache = probe_cache__new(target, NULL);
 	int ret = 0;
 
 	if (!cache) {
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index e0148b0..0a58287 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -534,13 +534,17 @@ char *build_id_cache__complement(const char *incomplete_sbuild_id)
 }
 
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
-			       bool is_kallsyms, bool is_vdso)
+			       struct nsinfo *nsi, bool is_kallsyms,
+			       bool is_vdso)
 {
 	char *realname = (char *)name, *filename;
 	bool slash = is_kallsyms || is_vdso;
+	struct nscookie nsc;
 
 	if (!slash) {
+		nsinfo__mountns_enter(nsi, &nsc);
 		realname = realpath(name, NULL);
+		nsinfo__mountns_exit(&nsc);
 		if (!realname)
 			return NULL;
 	}
@@ -556,13 +560,13 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
 	return filename;
 }
 
-int build_id_cache__list_build_ids(const char *pathname,
+int build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi,
 				   struct strlist **result)
 {
 	char *dir_name;
 	int ret = 0;
 
-	dir_name = build_id_cache__cachedir(NULL, pathname, false, false);
+	dir_name = build_id_cache__cachedir(NULL, pathname, nsi, false, false);
 	if (!dir_name)
 		return -ENOMEM;
 
@@ -576,16 +580,20 @@ int build_id_cache__list_build_ids(const char *pathname,
 
 #if defined(HAVE_LIBELF_SUPPORT) && defined(HAVE_GELF_GETNOTE_SUPPORT)
 static int build_id_cache__add_sdt_cache(const char *sbuild_id,
-					  const char *realname)
+					  const char *realname,
+					  struct nsinfo *nsi)
 {
 	struct probe_cache *cache;
 	int ret;
+	struct nscookie nsc;
 
-	cache = probe_cache__new(sbuild_id);
+	cache = probe_cache__new(sbuild_id, nsi);
 	if (!cache)
 		return -1;
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = probe_cache__scan_sdt(cache, realname);
+	nsinfo__mountns_exit(&nsc);
 	if (ret >= 0) {
 		pr_debug4("Found %d SDTs in %s\n", ret, realname);
 		if (probe_cache__commit(cache) < 0)
@@ -595,25 +603,30 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id,
 	return ret;
 }
 #else
-#define build_id_cache__add_sdt_cache(sbuild_id, realname) (0)
+#define build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) (0)
 #endif
 
 int build_id_cache__add_s(const char *sbuild_id, const char *name,
-			  bool is_kallsyms, bool is_vdso)
+			  struct nsinfo *nsi, bool is_kallsyms, bool is_vdso)
 {
 	const size_t size = PATH_MAX;
 	char *realname = NULL, *filename = NULL, *dir_name = NULL,
 	     *linkname = zalloc(size), *tmp;
 	int err = -1;
+	struct nscookie nsc;
 
 	if (!is_kallsyms) {
+		if (!is_vdso)
+			nsinfo__mountns_enter(nsi, &nsc);
 		realname = realpath(name, NULL);
+		if (!is_vdso)
+			nsinfo__mountns_exit(&nsc);
 		if (!realname)
 			goto out_free;
 	}
 
-	dir_name = build_id_cache__cachedir(sbuild_id, name,
-					    is_kallsyms, is_vdso);
+	dir_name = build_id_cache__cachedir(sbuild_id, name, nsi, is_kallsyms,
+					    is_vdso);
 	if (!dir_name)
 		goto out_free;
 
@@ -634,7 +647,10 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 
 	if (access(filename, F_OK)) {
 		if (is_kallsyms) {
-			 if (copyfile("/proc/kallsyms", filename))
+			if (copyfile("/proc/kallsyms", filename))
+				goto out_free;
+		} else if (nsi && nsi->need_setns) {
+			if (copyfile_ns(name, filename, nsi))
 				goto out_free;
 		} else if (link(realname, filename) && errno != EEXIST &&
 				copyfile(name, filename))
@@ -657,7 +673,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 		err = 0;
 
 	/* Update SDT cache : error is just warned */
-	if (realname && build_id_cache__add_sdt_cache(sbuild_id, realname) < 0)
+	if (realname &&
+	    build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) < 0)
 		pr_debug4("Failed to update/scan SDT cache for %s\n", realname);
 
 out_free:
@@ -670,14 +687,15 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 }
 
 static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
-				 const char *name, bool is_kallsyms,
-				 bool is_vdso)
+				 const char *name, struct nsinfo *nsi,
+				 bool is_kallsyms, bool is_vdso)
 {
 	char sbuild_id[SBUILD_ID_SIZE];
 
 	build_id__sprintf(build_id, build_id_size, sbuild_id);
 
-	return build_id_cache__add_s(sbuild_id, name, is_kallsyms, is_vdso);
+	return build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms,
+				     is_vdso);
 }
 
 bool build_id_cache__cached(const char *sbuild_id)
@@ -743,7 +761,7 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine)
 		name = nm;
 	}
 	return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
-				     is_kallsyms, is_vdso);
+				     dso->nsinfo, is_kallsyms, is_vdso);
 }
 
 static int __dsos__cache_build_ids(struct list_head *head,
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 96690a5..2397084 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -5,6 +5,7 @@
 #define SBUILD_ID_SIZE	(BUILD_ID_SIZE * 2 + 1)
 
 #include "tool.h"
+#include "namespaces.h"
 #include <linux/types.h>
 
 extern struct perf_tool build_id__mark_dso_hit_ops;
@@ -31,17 +32,19 @@ int perf_session__cache_build_ids(struct perf_session *session);
 char *build_id_cache__origname(const char *sbuild_id);
 char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size);
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
-			       bool is_kallsyms, bool is_vdso);
+			       struct nsinfo *nsi, bool is_kallsyms,
+			       bool is_vdso);
 
 struct strlist;
 
 struct strlist *build_id_cache__list_all(bool validonly);
 char *build_id_cache__complement(const char *incomplete_sbuild_id);
-int build_id_cache__list_build_ids(const char *pathname,
+int build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
 int build_id_cache__add_s(const char *sbuild_id,
-			  const char *name, bool is_kallsyms, bool is_vdso);
+			  const char *name, struct nsinfo *nsi,
+			  bool is_kallsyms, bool is_vdso);
 int build_id_cache__remove_s(const char *sbuild_id);
 
 extern char buildid_dir[];
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index beda40e..dc9b495 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -504,7 +504,14 @@ static void check_data_close(void);
  */
 static int open_dso(struct dso *dso, struct machine *machine)
 {
-	int fd = __open_dso(dso, machine);
+	int fd;
+	struct nscookie nsc;
+
+	if (dso->binary_type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		nsinfo__mountns_enter(dso->nsinfo, &nsc);
+	fd = __open_dso(dso, machine);
+	if (dso->binary_type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		nsinfo__mountns_exit(&nsc);
 
 	if (fd >= 0) {
 		dso__list_add(dso);
@@ -1302,6 +1309,7 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
 {
 	bool have_build_id = false;
 	struct dso *pos;
+	struct nscookie nsc;
 
 	list_for_each_entry(pos, head, node) {
 		if (with_hits && !pos->hit && !dso__is_vdso(pos))
@@ -1310,11 +1318,13 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
 			have_build_id = true;
 			continue;
 		}
+		nsinfo__mountns_enter(pos->nsinfo, &nsc);
 		if (filename__read_build_id(pos->long_name, pos->build_id,
 					    sizeof(pos->build_id)) > 0) {
 			have_build_id	  = true;
 			pos->has_build_id = true;
 		}
+		nsinfo__mountns_exit(&nsc);
 	}
 
 	return have_build_id;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 01e779b..84e3010 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -2124,7 +2124,7 @@ void print_sdt_events(const char *subsys_glob, const char *event_glob,
 		return;
 	}
 	strlist__for_each_entry(nd, bidlist) {
-		pcache = probe_cache__new(nd->s);
+		pcache = probe_cache__new(nd->s, NULL);
 		if (!pcache)
 			continue;
 		list_for_each_entry(ent, &pcache->entries, node) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index dce4f12..db67029 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2768,7 +2768,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	if (ret == -EINVAL && pev->uprobes)
 		warn_uprobe_event_compat(tev);
 	if (ret == 0 && probe_conf.cache) {
-		cache = probe_cache__new(pev->target);
+		cache = probe_cache__new(pev->target, pev->nsi);
 		if (!cache ||
 		    probe_cache__add_entry(cache, pev, tevs, ntevs) < 0 ||
 		    probe_cache__commit(cache) < 0)
@@ -3118,7 +3118,7 @@ static int find_cached_events(struct perf_probe_event *pev,
 	int ntevs = 0;
 	int ret = 0;
 
-	cache = probe_cache__new(target);
+	cache = probe_cache__new(target, pev->nsi);
 	/* Return 0 ("not found") if the target has no probe cache. */
 	if (!cache)
 		return 0;
@@ -3208,7 +3208,7 @@ static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
 		else
 			return find_cached_events(pev, tevs, pev->target);
 	}
-	cache = probe_cache__new(pev->target);
+	cache = probe_cache__new(pev->target, pev->nsi);
 	if (!cache)
 		return 0;
 
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
index d679389..cdf8d83 100644
--- a/tools/perf/util/probe-file.c
+++ b/tools/perf/util/probe-file.c
@@ -412,13 +412,15 @@ int probe_cache_entry__get_event(struct probe_cache_entry *entry,
 }
 
 /* For the kernel probe caches, pass target = NULL or DSO__NAME_KALLSYMS */
-static int probe_cache__open(struct probe_cache *pcache, const char *target)
+static int probe_cache__open(struct probe_cache *pcache, const char *target,
+			     struct nsinfo *nsi)
 {
 	char cpath[PATH_MAX];
 	char sbuildid[SBUILD_ID_SIZE];
 	char *dir_name = NULL;
 	bool is_kallsyms = false;
 	int ret, fd;
+	struct nscookie nsc;
 
 	if (target && build_id_cache__cached(target)) {
 		/* This is a cached buildid */
@@ -431,8 +433,11 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 		target = DSO__NAME_KALLSYMS;
 		is_kallsyms = true;
 		ret = sysfs__sprintf_build_id("/", sbuildid);
-	} else
+	} else {
+		nsinfo__mountns_enter(nsi, &nsc);
 		ret = filename__sprintf_build_id(target, sbuildid);
+		nsinfo__mountns_exit(&nsc);
+	}
 
 	if (ret < 0) {
 		pr_debug("Failed to get build-id from %s.\n", target);
@@ -441,7 +446,7 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 
 	/* If we have no buildid cache, make it */
 	if (!build_id_cache__cached(sbuildid)) {
-		ret = build_id_cache__add_s(sbuildid, target,
+		ret = build_id_cache__add_s(sbuildid, target, nsi,
 					    is_kallsyms, NULL);
 		if (ret < 0) {
 			pr_debug("Failed to add build-id cache: %s\n", target);
@@ -449,7 +454,7 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 		}
 	}
 
-	dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms,
+	dir_name = build_id_cache__cachedir(sbuildid, target, nsi, is_kallsyms,
 					    false);
 found:
 	if (!dir_name) {
@@ -554,7 +559,7 @@ void probe_cache__delete(struct probe_cache *pcache)
 	free(pcache);
 }
 
-struct probe_cache *probe_cache__new(const char *target)
+struct probe_cache *probe_cache__new(const char *target, struct nsinfo *nsi)
 {
 	struct probe_cache *pcache = probe_cache__alloc();
 	int ret;
@@ -562,7 +567,7 @@ struct probe_cache *probe_cache__new(const char *target)
 	if (!pcache)
 		return NULL;
 
-	ret = probe_cache__open(pcache, target);
+	ret = probe_cache__open(pcache, target, nsi);
 	if (ret < 0) {
 		pr_debug("Cache open error: %d\n", ret);
 		goto out_err;
@@ -974,7 +979,7 @@ int probe_cache__show_all_caches(struct strfilter *filter)
 		return -EINVAL;
 	}
 	strlist__for_each_entry(nd, bidlist) {
-		pcache = probe_cache__new(nd->s);
+		pcache = probe_cache__new(nd->s, NULL);
 		if (!pcache)
 			continue;
 		if (!list_empty(&pcache->entries)) {
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h
index 5ecc9d3..2ca4163 100644
--- a/tools/perf/util/probe-file.h
+++ b/tools/perf/util/probe-file.h
@@ -51,7 +51,7 @@ int probe_file__del_strlist(int fd, struct strlist *namelist);
 int probe_cache_entry__get_event(struct probe_cache_entry *entry,
 				 struct probe_trace_event **tevs);
 
-struct probe_cache *probe_cache__new(const char *target);
+struct probe_cache *probe_cache__new(const char *target, struct nsinfo *nsi);
 int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct perf_probe_event *pev,
 			   struct probe_trace_event *tevs, int ntevs);
@@ -69,7 +69,7 @@ int probe_cache__show_all_caches(struct strfilter *filter);
 bool probe_type_is_available(enum probe_type type);
 bool kretprobe_offset_is_supported(void);
 #else	/* ! HAVE_LIBELF_SUPPORT */
-static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused)
+static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused)
 {
 	return NULL;
 }
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 97e454f..9f45bcd 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1464,7 +1464,6 @@ static int dso__find_perf_map(char *filebuf, size_t bufsz,
 	return rc;
 }
 
-
 int dso__load(struct dso *dso, struct map *map)
 {
 	char *name;
@@ -1566,6 +1565,8 @@ int dso__load(struct dso *dso, struct map *map)
 	for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) {
 		struct symsrc *ss = &ss_[ss_pos];
 		bool next_slot = false;
+		bool is_reg;
+		int sirc;
 
 		enum dso_binary_type symtab_type = binary_type_symtab[i];
 
@@ -1576,12 +1577,20 @@ int dso__load(struct dso *dso, struct map *map)
 						   root_dir, name, PATH_MAX))
 			continue;
 
-		if (!is_regular_file(name))
-			continue;
+		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+			nsinfo__mountns_exit(&nsc);
+
+		is_reg = is_regular_file(name);
+		sirc = symsrc__init(ss, dso, name, symtab_type);
 
-		/* Name is now the name of the next image to try */
-		if (symsrc__init(ss, dso, name, symtab_type) < 0)
+		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+			nsinfo__mountns_enter(dso->nsinfo, &nsc);
+
+		if (!is_reg || sirc < 0) {
+			if (sirc >= 0)
+				symsrc__destroy(ss);
 			continue;
+		}
 
 		if (!syms_ss && symsrc__has_symtab(ss)) {
 			syms_ss = ss;
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 988111e..9e4ea85 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -143,13 +143,17 @@ struct strlist *lsdir(const char *name,
 	return list;
 }
 
-static int slow_copyfile(const char *from, const char *to)
+static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)
 {
 	int err = -1;
 	char *line = NULL;
 	size_t n;
-	FILE *from_fp = fopen(from, "r"), *to_fp;
+	FILE *from_fp, *to_fp;
+	struct nscookie nsc;
 
+	nsinfo__mountns_enter(nsi, &nsc);
+	from_fp = fopen(from, "r");
+	nsinfo__mountns_exit(&nsc);
 	if (from_fp == NULL)
 		goto out;
 
@@ -198,15 +202,21 @@ int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
 	return size ? -1 : 0;
 }
 
-int copyfile_mode(const char *from, const char *to, mode_t mode)
+static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
+			    struct nsinfo *nsi)
 {
 	int fromfd, tofd;
 	struct stat st;
-	int err = -1;
+	int err;
 	char *tmp = NULL, *ptr = NULL;
+	struct nscookie nsc;
 
-	if (stat(from, &st))
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = stat(from, &st);
+	nsinfo__mountns_exit(&nsc);
+	if (err)
 		goto out;
+	err = -1;
 
 	/* extra 'x' at the end is to reserve space for '.' */
 	if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
@@ -227,11 +237,13 @@ int copyfile_mode(const char *from, const char *to, mode_t mode)
 		goto out_close_to;
 
 	if (st.st_size == 0) { /* /proc? do it slowly... */
-		err = slow_copyfile(from, tmp);
+		err = slow_copyfile(from, tmp, nsi);
 		goto out_close_to;
 	}
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	fromfd = open(from, O_RDONLY);
+	nsinfo__mountns_exit(&nsc);
 	if (fromfd < 0)
 		goto out_close_to;
 
@@ -248,6 +260,16 @@ int copyfile_mode(const char *from, const char *to, mode_t mode)
 	return err;
 }
 
+int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi)
+{
+	return copyfile_mode_ns(from, to, 0755, nsi);
+}
+
+int copyfile_mode(const char *from, const char *to, mode_t mode)
+{
+	return copyfile_mode_ns(from, to, mode, NULL);
+}
+
 int copyfile(const char *from, const char *to)
 {
 	return copyfile_mode(from, to, 0755);
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 978572d..a5f80c2 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -12,6 +12,7 @@
 #include <stdarg.h>
 #include <linux/compiler.h>
 #include <linux/types.h>
+#include "namespaces.h"
 
 /* General helper functions */
 void usage(const char *err) __noreturn;
@@ -37,6 +38,7 @@ struct strlist *lsdir(const char *name, bool (*filter)(const char *, struct dire
 bool lsdir_no_dot_filter(const char *name, struct dirent *d);
 int copyfile(const char *from, const char *to);
 int copyfile_mode(const char *from, const char *to, mode_t mode);
+int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi);
 int copyfile_offset(int fromfd, loff_t from_ofs, int tofd, loff_t to_ofs, u64 size);
 
 ssize_t readn(int fd, void *buf, size_t n);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH tip/perf/core 5/7] perf top: support lookup of symbols in other mount namespaces.
  2017-07-01  2:18 [PATCH tip/perf/core 0/7] namespace tracing improvements Krister Johansen
                   ` (3 preceding siblings ...)
  2017-07-01  2:18 ` [PATCH tip/perf/core 4/7] perf buildid-cache: support binary objects from other namespaces Krister Johansen
@ 2017-07-01  2:18 ` Krister Johansen
  2017-07-01  2:18 ` [PATCH tip/perf/core 6/7] perf documentation: updates for target-ns Krister Johansen
  2017-07-01  2:18 ` [PATCH tip/perf/core 7/7] perf buildid-cache: cache debuginfo Krister Johansen
  6 siblings, 0 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-01  2:18 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo
  Cc: Alexander Shishkin, linux-kernel, Krister Johansen

The perf top command needs to unshare its fs from the helper threads
in order to successfully setns(2) during its symbol lookup.  It also
needs to impelement a force flag to ignore ownership of perf-<pid>.map
files.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/builtin-top.c | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 2bcfa46..c918fef 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -586,6 +586,13 @@ static void *display_thread_tui(void *arg)
 		.refresh	= top->delay_secs,
 	};
 
+	/* In order to read symbols from other namespaces perf to  needs to call
+	 * setns(2).  This isn't permitted if the struct_fs has multiple users.
+	 * unshare(2) the fs so that we may continue to setns into namespaces
+	 * that we're observing.
+	 */
+	(void) unshare(CLONE_FS);
+
 	perf_top__sort_new_samples(top);
 
 	/*
@@ -627,6 +634,13 @@ static void *display_thread(void *arg)
 	struct perf_top *top = arg;
 	int delay_msecs, c;
 
+	/* In order to read symbols from other namespaces perf to  needs to call
+	 * setns(2).  This isn't permitted if the struct_fs has multiple users.
+	 * unshare(2) the fs so that we may continue to setns into namespaces
+	 * that we're observing.
+	 */
+	(void) unshare(CLONE_FS);
+
 	display_setup_sig();
 	pthread__unblock_sigwinch();
 repeat:
@@ -1205,6 +1219,7 @@ int cmd_top(int argc, const char **argv)
 		    "Show raw trace event output (do not use print fmt or plugins)"),
 	OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy,
 		    "Show entries in a hierarchy"),
+	OPT_BOOLEAN(0, "force", &symbol_conf.force, "don't complain, do it"),
 	OPT_END()
 	};
 	const char * const top_usage[] = {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH tip/perf/core 6/7] perf documentation: updates for target-ns.
  2017-07-01  2:18 [PATCH tip/perf/core 0/7] namespace tracing improvements Krister Johansen
                   ` (4 preceding siblings ...)
  2017-07-01  2:18 ` [PATCH tip/perf/core 5/7] perf top: support lookup of symbols in other mount namespaces Krister Johansen
@ 2017-07-01  2:18 ` Krister Johansen
  2017-07-03 18:48   ` Arnaldo Carvalho de Melo
  2017-07-01  2:18 ` [PATCH tip/perf/core 7/7] perf buildid-cache: cache debuginfo Krister Johansen
  6 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-01  2:18 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo
  Cc: Alexander Shishkin, linux-kernel, Krister Johansen

Update the perf documentation to describe the --target-ns option to
probe and buildid-cache.  Note the existence of the new --force flag
to top.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/Documentation/perf-buildid-cache.txt | 5 +++++
 tools/perf/Documentation/perf-probe.txt         | 5 +++++
 tools/perf/Documentation/perf-top.txt           | 4 ++++
 3 files changed, 14 insertions(+)

diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt
index 058064d..8468100 100644
--- a/tools/perf/Documentation/perf-buildid-cache.txt
+++ b/tools/perf/Documentation/perf-buildid-cache.txt
@@ -61,6 +61,11 @@ OPTIONS
 --verbose::
 	Be more verbose.
 
+--target-ns=PID:
+	Obtain mount namespace information from the target pid.  This is
+	used when creating a uprobe for a process that resides in a
+	different mount namespace from the perf(1) utility.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 165c2b1..c1a126b 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -130,6 +130,11 @@ OPTIONS
 --max-probes=NUM::
 	Set the maximum number of probe points for an event. Default is 128.
 
+--target-ns=PID:
+	Obtain mount namespace information from the target pid.  This is
+	used when creating a uprobe for a process that resides in a
+	different mount namespace from the perf(1) utility.
+
 -x::
 --exec=PATH::
 	Specify path to the executable or shared library file for user
diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index e71d638..d864ea6 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -237,6 +237,10 @@ Default is to monitor all CPUS.
 --hierarchy::
 	Enable hierarchy output.
 
+--force::
+	Don't do ownership validation.
+
+
 INTERACTIVE PROMPTING KEYS
 --------------------------
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH tip/perf/core 7/7] perf buildid-cache: cache debuginfo
  2017-07-01  2:18 [PATCH tip/perf/core 0/7] namespace tracing improvements Krister Johansen
                   ` (5 preceding siblings ...)
  2017-07-01  2:18 ` [PATCH tip/perf/core 6/7] perf documentation: updates for target-ns Krister Johansen
@ 2017-07-01  2:18 ` Krister Johansen
  6 siblings, 0 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-01  2:18 UTC (permalink / raw)
  To: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo
  Cc: Alexander Shishkin, linux-kernel, Krister Johansen

If a stripped binary is placed in the cache, the user is in a situation
where there's a cached elf file present, but it doesn't have any symtab
to use for name resolution.  Grab the debuginfo for binaries that don't
end in .ko.  This yields a better chance of resolving symbols from
older traces.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/builtin-buildid-cache.c |  2 +-
 tools/perf/util/annotate.c         |  2 +-
 tools/perf/util/build-id.c         | 72 +++++++++++++++++++++++++++++++++++---
 tools/perf/util/build-id.h         |  3 +-
 tools/perf/util/dso.c              |  8 ++++-
 tools/perf/util/dso.h              |  1 +
 tools/perf/util/machine.c          |  3 +-
 tools/perf/util/symbol.c           | 12 +++++--
 8 files changed, 90 insertions(+), 13 deletions(-)

diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 0b9a43e..2e35d80 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -243,7 +243,7 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
 	char filename[PATH_MAX];
 	u8 build_id[BUILD_ID_SIZE];
 
-	if (dso__build_id_filename(dso, filename, sizeof(filename)) &&
+	if (dso__build_id_filename(dso, filename, sizeof(filename), false) &&
 	    filename__read_build_id(filename, build_id,
 				    sizeof(build_id)) != sizeof(build_id)) {
 		if (errno == ENOENT)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index be1caab..7ce940d 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1327,7 +1327,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
 	    !dso__is_kcore(dso))
 		return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX;
 
-	build_id_filename = dso__build_id_filename(dso, NULL, 0);
+	build_id_filename = dso__build_id_filename(dso, NULL, 0, false);
 	if (build_id_filename) {
 		__symbol__join_symfs(filename, filename_size, build_id_filename);
 		free(build_id_filename);
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index 0a58287..789d20a 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -243,12 +243,15 @@ static bool build_id_cache__valid_id(char *sbuild_id)
 	return result;
 }
 
-static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso)
+static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso,
+					    bool is_debug)
 {
-	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf");
+	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : (is_debug ?
+	    "debug" : "elf"));
 }
 
-char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
+char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size,
+			     bool is_debug)
 {
 	bool is_kallsyms = dso__is_kallsyms((struct dso *)dso);
 	bool is_vdso = dso__is_vdso((struct dso *)dso);
@@ -270,7 +273,8 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
 		ret = asnprintf(&bf, size, "%s", linkname);
 	else
 		ret = asnprintf(&bf, size, "%s/%s", linkname,
-			 build_id_cache__basename(is_kallsyms, is_vdso));
+			 build_id_cache__basename(is_kallsyms, is_vdso,
+						  is_debug));
 	if (ret < 0 || (!alloc && size < (unsigned int)ret))
 		bf = NULL;
 	free(linkname);
@@ -606,12 +610,40 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id,
 #define build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) (0)
 #endif
 
+static char *build_id_cache__find_debug(const char *sbuild_id,
+					struct nsinfo *nsi)
+{
+	char *realname = NULL;
+	char *debugfile;
+	struct nscookie nsc;
+	size_t len = 0;
+
+	debugfile = calloc(1, PATH_MAX);
+	if (!debugfile)
+		goto out;
+
+	len = __symbol__join_symfs(debugfile, PATH_MAX,
+				   "/usr/lib/debug/.build-id/");
+	snprintf(debugfile + len, PATH_MAX - len, "%.2s/%s.debug", sbuild_id,
+		 sbuild_id + 2);
+
+	nsinfo__mountns_enter(nsi, &nsc);
+	realname = realpath(debugfile, NULL);
+	if (realname && access(realname, R_OK))
+		zfree(&realname);
+	nsinfo__mountns_exit(&nsc);
+out:
+	free(debugfile);
+	return realname;
+}
+
 int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			  struct nsinfo *nsi, bool is_kallsyms, bool is_vdso)
 {
 	const size_t size = PATH_MAX;
 	char *realname = NULL, *filename = NULL, *dir_name = NULL,
 	     *linkname = zalloc(size), *tmp;
+	char *debugfile = NULL;
 	int err = -1;
 	struct nscookie nsc;
 
@@ -640,7 +672,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 
 	/* Save the allocated buildid dirname */
 	if (asprintf(&filename, "%s/%s", dir_name,
-		     build_id_cache__basename(is_kallsyms, is_vdso)) < 0) {
+		     build_id_cache__basename(is_kallsyms, is_vdso,
+		     false)) < 0) {
 		filename = NULL;
 		goto out_free;
 	}
@@ -657,6 +690,34 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			goto out_free;
 	}
 
+	/* Some binaries are stripped, but have .debug files with their symbol
+	 * table.  Check to see if we can locate one of those, since the elf
+	 * file itself may not be very useful to users of our tools without a
+	 * symtab.
+	 */
+	if (!is_kallsyms && !is_vdso &&
+	    strncmp(".ko", name + strlen(name) - 3, 3)) {
+		debugfile = build_id_cache__find_debug(sbuild_id, nsi);
+		if (debugfile) {
+			zfree(&filename);
+			if (asprintf(&filename, "%s/%s", dir_name,
+			    build_id_cache__basename(false, false, true)) < 0) {
+				filename = NULL;
+				goto out_free;
+			}
+			if (access(filename, F_OK)) {
+				if (nsi && nsi->need_setns) {
+					if (copyfile_ns(debugfile, filename,
+							nsi))
+						goto out_free;
+				} else if (link(debugfile, filename) &&
+						errno != EEXIST &&
+						copyfile(debugfile, filename))
+					goto out_free;
+			}
+		}
+	}
+
 	if (!build_id_cache__linkname(sbuild_id, linkname, size))
 		goto out_free;
 	tmp = strrchr(linkname, '/');
@@ -681,6 +742,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 	if (!is_kallsyms)
 		free(realname);
 	free(filename);
+	free(debugfile);
 	free(dir_name);
 	free(linkname);
 	return err;
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 2397084..113dc06 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -17,7 +17,8 @@ int filename__sprintf_build_id(const char *pathname, char *sbuild_id);
 char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf,
 				    size_t size);
 
-char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size);
+char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size,
+			     bool is_debug);
 
 int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
 			   struct perf_sample *sample, struct perf_evsel *evsel,
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index dc9b495..b9e087f 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -32,6 +32,7 @@ char dso__symtab_origin(const struct dso *dso)
 		[DSO_BINARY_TYPE__JAVA_JIT]			= 'j',
 		[DSO_BINARY_TYPE__DEBUGLINK]			= 'l',
 		[DSO_BINARY_TYPE__BUILD_ID_CACHE]		= 'B',
+		[DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO]	= 'D',
 		[DSO_BINARY_TYPE__FEDORA_DEBUGINFO]		= 'f',
 		[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]		= 'u',
 		[DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO]	= 'o',
@@ -97,7 +98,12 @@ int dso__read_binary_type_filename(const struct dso *dso,
 		break;
 	}
 	case DSO_BINARY_TYPE__BUILD_ID_CACHE:
-		if (dso__build_id_filename(dso, filename, size) == NULL)
+		if (dso__build_id_filename(dso, filename, size, false) == NULL)
+			ret = -1;
+		break;
+
+	case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+		if (dso__build_id_filename(dso, filename, size, true) == NULL)
 			ret = -1;
 		break;
 
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 78ec637..f886141 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -21,6 +21,7 @@ enum dso_binary_type {
 	DSO_BINARY_TYPE__JAVA_JIT,
 	DSO_BINARY_TYPE__DEBUGLINK,
 	DSO_BINARY_TYPE__BUILD_ID_CACHE,
+	DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO,
 	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
 	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
 	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index ed98881..bf60709 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -705,7 +705,8 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp)
 
 	if (kdso->has_build_id) {
 		char filename[PATH_MAX];
-		if (dso__build_id_filename(kdso, filename, sizeof(filename)))
+		if (dso__build_id_filename(kdso, filename, sizeof(filename),
+					   false))
 			printed += fprintf(fp, "[0] %s\n", filename);
 	}
 
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 9f45bcd..0a12625 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -53,6 +53,7 @@ static enum dso_binary_type binary_type_symtab[] = {
 	DSO_BINARY_TYPE__JAVA_JIT,
 	DSO_BINARY_TYPE__DEBUGLINK,
 	DSO_BINARY_TYPE__BUILD_ID_CACHE,
+	DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO,
 	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
 	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
 	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
@@ -1418,6 +1419,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
 		return kmod && dso->symtab_type == type;
 
 	case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+	case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
 		return true;
 
 	case DSO_BINARY_TYPE__NOT_FOUND:
@@ -1566,10 +1568,14 @@ int dso__load(struct dso *dso, struct map *map)
 		struct symsrc *ss = &ss_[ss_pos];
 		bool next_slot = false;
 		bool is_reg;
+		bool nsexit;
 		int sirc;
 
 		enum dso_binary_type symtab_type = binary_type_symtab[i];
 
+		nsexit = (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE ||
+		    symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO);
+
 		if (!dso__is_compatible_symtab_type(dso, kmod, symtab_type))
 			continue;
 
@@ -1577,13 +1583,13 @@ int dso__load(struct dso *dso, struct map *map)
 						   root_dir, name, PATH_MAX))
 			continue;
 
-		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		if (nsexit)
 			nsinfo__mountns_exit(&nsc);
 
 		is_reg = is_regular_file(name);
 		sirc = symsrc__init(ss, dso, name, symtab_type);
 
-		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		if (nsexit)
 			nsinfo__mountns_enter(dso->nsinfo, &nsc);
 
 		if (!is_reg || sirc < 0) {
@@ -1725,7 +1731,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map)
 	}
 
 	if (!symbol_conf.ignore_vmlinux_buildid)
-		filename = dso__build_id_filename(dso, NULL, 0);
+		filename = dso__build_id_filename(dso, NULL, 0, false);
 	if (filename != NULL) {
 		err = dso__load_vmlinux(dso, map, filename, true);
 		if (err > 0)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH tip/perf/core 1/7] perf symbols: find symbols in different mount namespace
  2017-07-01  2:18 ` [PATCH tip/perf/core 1/7] perf symbols: find symbols in different mount namespace Krister Johansen
@ 2017-07-03 18:38   ` Arnaldo Carvalho de Melo
  2017-07-05 20:44     ` Krister Johansen
  0 siblings, 1 reply; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-03 18:38 UTC (permalink / raw)
  To: Krister Johansen
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel

Em Fri, Jun 30, 2017 at 07:18:53PM -0700, Krister Johansen escreveu:
> 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.

Clear implementation overall, followed tools/perf/ coding style, uses
reference counts, etc, great! some comments below
 
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> ---
>  tools/perf/util/dso.c        |   1 +
>  tools/perf/util/dso.h        |   2 +
>  tools/perf/util/map.c        |   2 +
>  tools/perf/util/namespaces.c | 127 +++++++++++++++++++++++++++++++++++++++++++
>  tools/perf/util/namespaces.h |  33 +++++++++++
>  tools/perf/util/symbol.c     |  11 ++++
>  tools/perf/util/thread.c     |   3 +
>  tools/perf/util/thread.h     |   1 +
>  8 files changed, 180 insertions(+)
> 
> diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
> index 4e7ab61..beda40e 100644
> --- a/tools/perf/util/dso.c
> +++ b/tools/perf/util/dso.c
> @@ -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);
>  }
> diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
> index bd061ba..78ec637 100644
> --- a/tools/perf/util/dso.h
> +++ b/tools/perf/util/dso.h
> @@ -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];
>  };
> diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
> index 2179b2d..5dc60ca 100644
> --- a/tools/perf/util/map.c
> +++ b/tools/perf/util/map.c
> @@ -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;
> diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
> index 67dcbcc..d7f31e0 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)
> +{
> +	free(nsi->mntns_path);

	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;
> +
> +	(void) setns(nc->oldns, CLONE_NEWNS);

We haven't been using (void) in these cases, is this to silence warnings
about not checking the return of functions? Is this one with some
attribute for that? But you're using even in simpler things like
close()...

Also if we have multiple threads using this interface, wouldn't there be
races? Humm, from 'man setns()':

       Given a file descriptor referring to a namespace, reassociate the calling thread with that namespace.

Ok, so should be ok.

> +
> +	if (nc->oldns > -1) {
> +		(void) close(nc->oldns);
> +		nc->oldns = -1;
> +	}
> +
> +	if (nc->newns > -1) {
> +		(void) close(nc->newns);
> +		nc->newns = -1;
> +	}
> +}
> diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
> index 468f1e9..b20f6ea 100644
> --- a/tools/perf/util/namespaces.h
> +++ b/tools/perf/util/namespaces.h
> @@ -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 */
> diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
> index e7a98db..60a9eaa 100644
> --- a/tools/perf/util/symbol.c
> +++ b/tools/perf/util/symbol.c
> @@ -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 @@ int dso__load(struct dso *dso, struct map *map)
>  out:
>  	dso__set_loaded(dso, map->type);
>  	pthread_mutex_unlock(&dso->lock);
> +	nsinfo__mountns_exit(&nsc);
>  
>  	return ret;
>  }
> diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
> index 378c418..aee9a42 100644
> --- a/tools/perf/util/thread.c
> +++ b/tools/perf/util/thread.c
> @@ -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);
>  }
> diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
> index 4eb849e..cb1a5dd 100644
> --- a/tools/perf/util/thread.h
> +++ b/tools/perf/util/thread.h
> @@ -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;
> -- 
> 2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH tip/perf/core 2/7] perf maps: lookup maps in both intitial mountns and inner mountns.
  2017-07-01  2:18 ` [PATCH tip/perf/core 2/7] perf maps: lookup maps in both intitial mountns and inner mountns Krister Johansen
@ 2017-07-03 18:44   ` Arnaldo Carvalho de Melo
  0 siblings, 0 replies; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-03 18:44 UTC (permalink / raw)
  To: Krister Johansen
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel

Em Fri, Jun 30, 2017 at 07:18:54PM -0700, Krister Johansen escreveu:
> If a process is in a mountns and has symbols in /tmp/perf-<pid>.map,
> look first in the namespace using the tgid for the pidns that the
> process might be in.  If the map isn't found there, try looking in
> the mountns where perf is running, and use the tgid that's appropriate
> for perf's pid namespace.  If all else fails, use the original pid.
> 
> This allows us to locate a symbol map file in the mount namespace, if
> it was generated there.  However, we also try the tool's /tmp in case
> it's there instead.
> 
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> ---
>  tools/perf/util/machine.c    | 19 ++++------
>  tools/perf/util/map.c        | 29 ++++++++++++----
>  tools/perf/util/map.h        |  8 ++---
>  tools/perf/util/namespaces.c | 83 ++++++++++++++++++++++++++++++++++++++++----
>  tools/perf/util/namespaces.h |  5 ++-
>  tools/perf/util/symbol.c     | 71 ++++++++++++++++++++++++++++++-------
>  6 files changed, 172 insertions(+), 43 deletions(-)
> 
> diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
> index d7f31cb..ed98881 100644
> --- a/tools/perf/util/machine.c
> +++ b/tools/perf/util/machine.c
> @@ -1388,13 +1388,10 @@ int machine__process_mmap2_event(struct machine *machine,
>  	else
>  		type = MAP__FUNCTION;
>  
> -	map = map__new(machine, event->mmap2.start,
> -			event->mmap2.len, event->mmap2.pgoff,
> -			event->mmap2.pid, event->mmap2.maj,
> -			event->mmap2.min, event->mmap2.ino,
> -			event->mmap2.ino_generation,
> -			event->mmap2.prot,
> -			event->mmap2.flags,
> +	map = map__new(machine, event->mmap2.start, event->mmap2.len,
> +			event->mmap2.pgoff, event->mmap2.maj, event->mmap2.min,
> +			event->mmap2.ino, event->mmap2.ino_generation,
> +			event->mmap2.prot, event->mmap2.flags,
>  			event->mmap2.filename, type, thread);

Try not to reflow like that, it makes it harder to spot _just what
changed_, so you just removed the .pid parameter, ok, it should be:

	map = map__new(machine, event->mmap2.start,
			event->mmap2.len, event->mmap2.pgoff,
-			event->mmap2.pid, event->mmap2.maj,
+			event->mmap2.maj,
			event->mmap2.min, event->mmap2.ino,
			event->mmap2.ino_generation,
			event->mmap2.prot,
			event->mmap2.flags,
  			event->mmap2.filename, type, thread);

>  
>  	if (map == NULL)
> @@ -1446,11 +1443,9 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
>  	else
>  		type = MAP__FUNCTION;
>  
> -	map = map__new(machine, event->mmap.start,
> -			event->mmap.len, event->mmap.pgoff,
> -			event->mmap.pid, 0, 0, 0, 0, 0, 0,
> -			event->mmap.filename,
> -			type, thread);
> +	map = map__new(machine, event->mmap.start, event->mmap.len,
> +			event->mmap.pgoff, 0, 0, 0, 0, 0, 0,
> +			event->mmap.filename, type, thread);

Ditto.

>  
>  	if (map == NULL)
>  		goto out_problem_map;
> diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
> index 5dc60ca..2bd20cb 100644
> --- a/tools/perf/util/map.c
> +++ b/tools/perf/util/map.c
> @@ -145,12 +145,14 @@ void map__init(struct map *map, enum map_type type,
>  	refcount_set(&map->refcnt, 1);
>  }
>  
> -struct map *map__new(struct machine *machine, u64 start, u64 len,
> -		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
> -		     u64 ino_gen, u32 prot, u32 flags, char *filename,
> -		     enum map_type type, struct thread *thread)
> +struct map *map__new(struct machine *machine, u64 start, u64 len, u64 pgoff,
> +		     u32 d_maj, u32 d_min, u64 ino, u64 ino_gen, u32 prot,
> +		     u32 flags, char *filename, enum map_type type,
> +		     struct thread *thread)

Ditto

>  {
>  	struct map *map = malloc(sizeof(*map));
> +	struct nsinfo *nsi = NULL;
> +	struct nsinfo *nnsi;
>  
>  	if (map != NULL) {
>  		char newfilename[PATH_MAX];
> @@ -168,9 +170,11 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
>  		map->ino_generation = ino_gen;
>  		map->prot = prot;
>  		map->flags = flags;
> +		nsi = nsinfo__get(thread->nsinfo);
>  
> -		if ((anon || no_dso) && type == MAP__FUNCTION) {
> -			snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
> +		if ((anon || no_dso) && nsi && type == MAP__FUNCTION) {
> +			snprintf(newfilename, sizeof(newfilename),
> +				 "/tmp/perf-%d.map", nsi->pid);
>  			filename = newfilename;
>  		}
>  
> @@ -180,6 +184,16 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
>  		}
>  
>  		if (vdso) {
> +			/* The vdso maps are always on the host and not the
> +			 * container.  Ensure that we don't use setns to look
> +			 * them up.
> +			 */
> +			nnsi = nsinfo__copy(nsi);
> +			if (nnsi) {
> +				nsinfo__put(nsi);
> +				nnsi->need_setns = false;
> +				nsi = nnsi;
> +			}
>  			pgoff = 0;
>  			dso = machine__findnew_vdso(machine, thread);
>  		} else
> @@ -201,11 +215,12 @@ 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->nsinfo = nsi;
>  		dso__put(dso);
>  	}
>  	return map;
>  out_delete:
> +	nsinfo__put(nsi);
>  	free(map);
>  	return NULL;
>  }
> diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
> index f9e8ac8..bf015a93 100644
> --- a/tools/perf/util/map.h
> +++ b/tools/perf/util/map.h
> @@ -140,10 +140,10 @@ struct thread;
>  
>  void map__init(struct map *map, enum map_type type,
>  	       u64 start, u64 end, u64 pgoff, struct dso *dso);
> -struct map *map__new(struct machine *machine, u64 start, u64 len,
> -		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
> -		     u64 ino_gen, u32 prot, u32 flags,
> -		     char *filename, enum map_type type, struct thread *thread);
> +struct map *map__new(struct machine *machine, u64 start, u64 len, u64 pgoff,
> +		     u32 d_maj, u32 d_min, u64 ino, u64 ino_gen, u32 prot,
> +		     u32 flags, char *filename, enum map_type type,
> +		     struct thread *thread);

Ditto

>  struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
>  void map__delete(struct map *map);
>  struct map *map__clone(struct map *map);
> diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
> index d7f31e0..cad7d8b 100644
> --- a/tools/perf/util/namespaces.c
> +++ b/tools/perf/util/namespaces.c
> @@ -40,18 +40,23 @@ void namespaces__free(struct namespaces *namespaces)
>  	free(namespaces);
>  }
>  
> -void nsinfo__init(struct nsinfo *nsi)
> +int nsinfo__init(struct nsinfo *nsi)
>  {
>  	char oldns[PATH_MAX];
> +	char spath[PATH_MAX];
>  	char *newns = NULL;
> +	char *statln = NULL;
>  	struct stat old_stat;
>  	struct stat new_stat;
> +	FILE *f = NULL;
> +	size_t linesz = 0;
> +	int rv = -1;
>  
>  	if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
> -		return;
> +		return rv;
>  
>  	if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1)
> -		return;
> +		return rv;
>  
>  	if (stat(oldns, &old_stat) < 0)
>  		goto out;
> @@ -68,24 +73,89 @@ void nsinfo__init(struct nsinfo *nsi)
>  		newns = NULL;
>  	}
>  
> +	/* If we're dealing with a process that is in a different PID namespace,
> +	 * attempt to work out the innermost tgid for the process.
> +	 */
> +	if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsi->pid) >= PATH_MAX)
> +		goto out;
> +
> +	f = fopen(spath, "r");
> +	if (f == NULL)
> +		goto out;
> +
> +	while (getline(&statln, &linesz, f) != -1) {
> +		/* Use tgid if CONFIG_PID_NS is not defined. */
> +		if (strstr(statln, "Tgid:") != NULL) {
> +			nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'),
> +						     NULL, 10);
> +			nsi->nstgid = nsi->tgid;
> +		}
> +
> +		if (strstr(statln, "NStgid:") != NULL) {
> +			nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'),
> +						     NULL, 10);
> +			break;
> +		}
> +	}
> +	rv = 0;
> +
>  out:
> +	if (f != NULL)
> +		(void) fclose(f);
> +	free(statln);
>  	free(newns);
> +	return rv;
>  }
>  
>  struct nsinfo *nsinfo__new(pid_t pid)
>  {
> -	struct nsinfo *nsi = calloc(1, sizeof(*nsi));
> +	struct nsinfo *nsi;
>  
> +	if (pid == 0)
> +		return NULL;
> +
> +	nsi = calloc(1, sizeof(*nsi));
>  	if (nsi != NULL) {
>  		nsi->pid = pid;
> +		nsi->tgid = pid;
> +		nsi->nstgid = pid;
>  		nsi->need_setns = false;
> -		nsinfo__init(nsi);
> +		/* Init may fail if the process exits while we're trying to look
> +		 * at its proc information.  In that case, save the pid but
> +		 * don't try to enter the namespace.
> +		 */
> +		if (nsinfo__init(nsi) == -1)
> +			nsi->need_setns = false;
> +
>  		refcount_set(&nsi->refcnt, 1);
>  	}
>  
>  	return nsi;
>  }
>  
> +struct nsinfo *nsinfo__copy(struct nsinfo *nsi)
> +{
> +	struct nsinfo *nnsi;
> +
> +	nnsi = calloc(1, sizeof(*nnsi));
> +	if (nnsi != NULL) {
> +		nnsi->pid = nsi->pid;
> +		nnsi->tgid = nsi->tgid;
> +		nnsi->nstgid = nsi->nstgid;
> +		nnsi->need_setns = nsi->need_setns;
> +		if (nsi->mntns_path) {
> +			nnsi->mntns_path = strdup(nsi->mntns_path);
> +			if (!nnsi->mntns_path) {
> +				free(nnsi);
> +				return NULL;
> +			}
> +		}
> +		refcount_set(&nnsi->refcnt, 1);
> +	}
> +
> +	return nnsi;
> +}
> +
>  void nsinfo__delete(struct nsinfo *nsi)
>  {
>  	free(nsi->mntns_path);
> @@ -105,7 +175,8 @@ void nsinfo__put(struct nsinfo *nsi)
>  		nsinfo__delete(nsi);
>  }
>  
> -void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc)
> +void nsinfo__mountns_enter(struct nsinfo *nsi,
> +				  struct nscookie *nc)
>  {
>  	char curpath[PATH_MAX];
>  	int oldns = -1;
> diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
> index b20f6ea..f19aa41 100644
> --- a/tools/perf/util/namespaces.h
> +++ b/tools/perf/util/namespaces.h
> @@ -26,6 +26,8 @@ void namespaces__free(struct namespaces *namespaces);
>  
>  struct nsinfo {
>  	pid_t			pid;
> +	pid_t			tgid;
> +	pid_t			nstgid;
>  	bool			need_setns;
>  	char			*mntns_path;
>  	refcount_t		refcnt;
> @@ -36,8 +38,9 @@ struct nscookie {
>  	int			newns;
>  };
>  
> -void nsinfo__init(struct nsinfo *nsi);
> +int nsinfo__init(struct nsinfo *nsi);
>  struct nsinfo *nsinfo__new(pid_t pid);
> +struct nsinfo *nsinfo__copy(struct nsinfo *nsi);
>  void nsinfo__delete(struct nsinfo *nsi);
>  
>  struct nsinfo *nsinfo__get(struct nsinfo *nsi);
> diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
> index 60a9eaa..97e454f 100644
> --- a/tools/perf/util/symbol.c
> +++ b/tools/perf/util/symbol.c
> @@ -19,7 +19,6 @@
>  #include "strlist.h"
>  #include "intlist.h"
>  #include "namespaces.h"
> -#include "vdso.h"
>  #include "header.h"
>  #include "path.h"
>  #include "sane_ctype.h"
> @@ -1327,14 +1326,15 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
>  	return __dso__load_kallsyms(dso, filename, map, false);
>  }
>  
> -static int dso__load_perf_map(struct dso *dso, struct map *map)
> +static int dso__load_perf_map(const char *map_path, struct dso *dso,
> +			      struct map *map)
>  {
>  	char *line = NULL;
>  	size_t n;
>  	FILE *file;
>  	int nr_syms = 0;
>  
> -	file = fopen(dso->long_name, "r");
> +	file = fopen(map_path, "r");
>  	if (file == NULL)
>  		goto out_failure;
>  
> @@ -1426,6 +1426,45 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
>  	}
>  }
>  
> +/* Checks for the existence of the perf-<pid>.map file in two different
> + * locations.  First, if the process is a separate mount namespace, check in
> + * that namespace using the pid of the innermost pid namespace.  If's not in a
> + * namespace, or the file can't be found there, try in the mount namespace of
> + * the tracing process using our view of its pid.
> + */
> +static int dso__find_perf_map(char *filebuf, size_t bufsz,
> +			      struct nsinfo **nsip)
> +{
> +	struct nscookie nsc;
> +	struct nsinfo *nsi;
> +	struct nsinfo *nnsi;
> +	int rc = -1;
> +
> +	nsi = *nsip;
> +
> +	if (nsi->need_setns) {
> +		snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nsi->nstgid);
> +		nsinfo__mountns_enter(nsi, &nsc);
> +		rc = access(filebuf, R_OK);
> +		nsinfo__mountns_exit(&nsc);
> +		if (rc == 0)
> +			return rc;
> +	}
> +
> +	nnsi = nsinfo__copy(nsi);
> +	if (nnsi) {
> +		nsinfo__put(nsi);
> +
> +		nnsi->need_setns = false;
> +		snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nnsi->tgid);
> +		*nsip = nnsi;
> +		rc = 0;
> +	}
> +
> +	return rc;
> +}
> +
> +
>  int dso__load(struct dso *dso, struct map *map)
>  {
>  	char *name;
> @@ -1437,18 +1476,23 @@ int dso__load(struct dso *dso, struct map *map)
>  	struct symsrc ss_[2];
>  	struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
>  	bool kmod;
> +	bool perfmap;
>  	unsigned char build_id[BUILD_ID_SIZE];
>  	struct nscookie nsc;
> +	char newmapname[PATH_MAX];
> +	const char *map_path = dso->long_name;
> +
> +	perfmap = strncmp(dso->name, "/tmp/perf-", 10) == 0;
> +	if (perfmap) {
> +		if (dso->nsinfo && (dso__find_perf_map(newmapname,
> +		    sizeof(newmapname), &dso->nsinfo) == 0)) {
> +			map_path = newmapname;
> +		}
> +	}
>  
>  	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;
> @@ -1471,19 +1515,20 @@ int dso__load(struct dso *dso, struct map *map)
>  
>  	dso->adjust_symbols = 0;
>  
> -	if (strncmp(dso->name, "/tmp/perf-", 10) == 0) {
> +	if (perfmap) {
>  		struct stat st;
>  
> -		if (lstat(dso->name, &st) < 0)
> +		if (lstat(map_path, &st) < 0)
>  			goto out;
>  
>  		if (!symbol_conf.force && st.st_uid && (st.st_uid != geteuid())) {
>  			pr_warning("File %s not owned by current user or root, "
> -				   "ignoring it (use -f to override).\n", dso->name);
> +				   "ignoring it (use -f to override).\n",
> +				   map_path);

Keep on the same line, that way we see straight away that you're
replacing dso->name with map_path, as they will be aligned.

>  			goto out;
>  		}
>  
> -		ret = dso__load_perf_map(dso, map);
> +		ret = dso__load_perf_map(map_path, dso, map);
>  		dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT :
>  					     DSO_BINARY_TYPE__NOT_FOUND;
>  		goto out;
> -- 
> 2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH tip/perf/core 3/7] perf probe: allow placing uprobes in alternate namespaces.
  2017-07-01  2:18 ` [PATCH tip/perf/core 3/7] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
@ 2017-07-03 18:46   ` Arnaldo Carvalho de Melo
  2017-07-05 20:45     ` Krister Johansen
  0 siblings, 1 reply; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-03 18:46 UTC (permalink / raw)
  To: Krister Johansen
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel

Em Fri, Jun 30, 2017 at 07:18:55PM -0700, Krister Johansen escreveu:
> Teaches perf how to place a uprobe on a file that's in a different mount
> namespace.  The user must add the probe using the --target-ns argument
> to perf probe.  Once it has been placed, it may be recorded against
> without further namespace-specific commands.
> 
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> ---
>  tools/perf/builtin-probe.c    | 44 ++++++++++++++++++++++--
>  tools/perf/util/probe-event.c | 79 +++++++++++++++++++++++++++++--------------
>  tools/perf/util/probe-event.h | 10 ++++--
>  3 files changed, 101 insertions(+), 32 deletions(-)
> 
> diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
> index cf9f9e9..5ab2e00 100644
> --- a/tools/perf/builtin-probe.c
> +++ b/tools/perf/builtin-probe.c
> @@ -58,6 +58,7 @@ static struct {
>  	struct line_range line_range;
>  	char *target;
>  	struct strfilter *filter;
> +	struct nsinfo *nsi;
>  } params;
>  
>  /* Parse an event definition. Note that any error must die. */
> @@ -80,6 +81,9 @@ static int parse_probe_event(const char *str)
>  		params.target_used = true;
>  	}
>  
> +	if (params.nsi)
> +		pev->nsi = nsinfo__get(params.nsi);
> +
>  	/* Parse a perf-probe command into event */
>  	ret = parse_perf_probe_command(str, pev);
>  	pr_debug("%d arguments\n", pev->nargs);
> @@ -178,6 +182,7 @@ static int opt_set_target(const struct option *opt, const char *str,
>  {
>  	int ret = -ENOENT;
>  	char *tmp;
> +	struct nscookie nsc;
>  
>  	if  (str) {
>  		if (!strcmp(opt->long_name, "exec"))
> @@ -189,7 +194,9 @@ static int opt_set_target(const struct option *opt, const char *str,
>  
>  		/* Expand given path to absolute path, except for modulename */
>  		if (params.uprobes || strchr(str, '/')) {
> +			nsinfo__mountns_enter(params.nsi, &nsc);
>  			tmp = realpath(str, NULL);
> +			nsinfo__mountns_exit(&nsc);

Perhaps have a nsinfo__realpath()? Don't know if this will be used
elsewhere, but looks shorter.

>  			if (!tmp) {
>  				pr_warning("Failed to get the absolute path of %s: %m\n", str);
>  				return ret;
> @@ -208,6 +215,34 @@ static int opt_set_target(const struct option *opt, const char *str,
>  	return ret;
>  }
>  
> +static int opt_set_target_ns(const struct option *opt __maybe_unused,
> +			     const char *str, int unset __maybe_unused)
> +{
> +	int ret = -ENOENT;
> +	pid_t ns_pid;
> +	struct nsinfo *nsip;
> +
> +	if (str) {
> +		errno = 0;
> +		ns_pid = (pid_t)strtol(str, NULL, 10);
> +		if (errno != 0) {
> +			ret = -errno;
> +			pr_warning("Failed to parse %s as a pid: %s\n", str,
> +				   strerror(errno));
> +			return ret;
> +		}
> +		nsip = nsinfo__new(ns_pid);
> +		if (nsip && nsip->need_setns)
> +			params.nsi = nsinfo__get(nsip);
> +		nsinfo__put(nsip);
> +
> +		ret = 0;
> +	}
> +
> +	return ret;
> +}
> +
> +
>  /* Command option callbacks */
>  
>  #ifdef HAVE_DWARF_SUPPORT
> @@ -299,6 +334,7 @@ static void cleanup_params(void)
>  	line_range__clear(&params.line_range);
>  	free(params.target);
>  	strfilter__delete(params.filter);
> +	nsinfo__put(params.nsi);
>  	memset(&params, 0, sizeof(params));
>  }
>  
> @@ -554,6 +590,8 @@ __cmd_probe(int argc, const char **argv)
>  	OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
>  	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
>  		   "Look for files with symbols relative to this directory"),
> +	OPT_CALLBACK(0, "target-ns", NULL, "pid",
> +		     "target pid for namespace information", opt_set_target_ns),
>  	OPT_END()
>  	};
>  	int ret;
> @@ -634,15 +672,15 @@ __cmd_probe(int argc, const char **argv)
>  			pr_err_with_code("  Error: Failed to show event list.", ret);
>  		return ret;
>  	case 'F':
> -		ret = show_available_funcs(params.target, params.filter,
> -					params.uprobes);
> +		ret = show_available_funcs(params.target, params.nsi,
> +					   params.filter, params.uprobes);
>  		if (ret < 0)
>  			pr_err_with_code("  Error: Failed to show functions.", ret);
>  		return ret;
>  #ifdef HAVE_DWARF_SUPPORT
>  	case 'L':
>  		ret = show_line_range(&params.line_range, params.target,
> -				      params.uprobes);
> +				      params.nsi, params.uprobes);
>  		if (ret < 0)
>  			pr_err_with_code("  Error: Failed to show lines.", ret);
>  		return ret;
> diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
> index 84e7e69..dce4f12 100644
> --- a/tools/perf/util/probe-event.c
> +++ b/tools/perf/util/probe-event.c
> @@ -184,13 +184,19 @@ static struct map *kernel_get_module_map(const char *module)
>  	return NULL;
>  }
>  
> -struct map *get_target_map(const char *target, bool user)
> +struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user)
>  {
>  	/* Init maps of given executable or kernel */
> -	if (user)
> -		return dso__new_map(target);
> -	else
> +	if (user) {
> +		struct map *map;
> +
> +		map = dso__new_map(target);
> +		if (map && map->dso)
> +			map->dso->nsinfo = nsinfo__get(nsi);
> +		return map;
> +	} else {
>  		return kernel_get_module_map(target);
> +	}
>  }
>  
>  static int convert_exec_to_group(const char *exec, char **result)
> @@ -366,7 +372,8 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
>  static int find_alternative_probe_point(struct debuginfo *dinfo,
>  					struct perf_probe_point *pp,
>  					struct perf_probe_point *result,
> -					const char *target, bool uprobes)
> +					const char *target, struct nsinfo *nsi,
> +					bool uprobes)
>  {
>  	struct map *map = NULL;
>  	struct symbol *sym;
> @@ -377,7 +384,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
>  	if (!pp->function || pp->file)
>  		return -ENOTSUP;
>  
> -	map = get_target_map(target, uprobes);
> +	map = get_target_map(target, nsi, uprobes);
>  	if (!map)
>  		return -EINVAL;
>  
> @@ -421,8 +428,8 @@ static int get_alternative_probe_event(struct debuginfo *dinfo,
>  
>  	memcpy(tmp, &pev->point, sizeof(*tmp));
>  	memset(&pev->point, 0, sizeof(pev->point));
> -	ret = find_alternative_probe_point(dinfo, tmp, &pev->point,
> -					   pev->target, pev->uprobes);
> +	ret = find_alternative_probe_point(dinfo, tmp, &pev->point, pev->target,
> +					   pev->nsi, pev->uprobes);
>  	if (ret < 0)
>  		memcpy(&pev->point, tmp, sizeof(*tmp));
>  
> @@ -444,7 +451,7 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
>  	if (lr->end != INT_MAX)
>  		len = lr->end - lr->start;
>  	ret = find_alternative_probe_point(dinfo, &pp, &result,
> -					   target, user);
> +					   target, NULL, user);
>  	if (!ret) {
>  		lr->function = result.function;
>  		lr->file = result.file;
> @@ -457,12 +464,14 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
>  }
>  
>  /* Open new debuginfo of given module */
> -static struct debuginfo *open_debuginfo(const char *module, bool silent)
> +static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
> +					bool silent)
>  {
>  	const char *path = module;
>  	char reason[STRERR_BUFSIZE];
>  	struct debuginfo *ret = NULL;
>  	struct dso *dso = NULL;
> +	struct nscookie nsc;
>  	int err;
>  
>  	if (!module || !strchr(module, '/')) {
> @@ -480,6 +489,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
>  		}
>  		path = dso->long_name;
>  	}
> +	nsinfo__mountns_enter(nsi, &nsc);
>  	ret = debuginfo__new(path);
>  	if (!ret && !silent) {
>  		pr_warning("The %s file has no debug information.\n", path);
> @@ -489,6 +499,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
>  			pr_warning("Rebuild with -g, ");
>  		pr_warning("or install an appropriate debuginfo package.\n");
>  	}
> +	nsinfo__mountns_exit(&nsc);
>  	return ret;
>  }
>  
> @@ -516,7 +527,7 @@ static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
>  		goto out;
>  	}
>  
> -	debuginfo_cache = open_debuginfo(module, silent);
> +	debuginfo_cache = open_debuginfo(module, NULL, silent);
>  	if (!debuginfo_cache)
>  		zfree(&debuginfo_cache_path);
>  out:
> @@ -531,14 +542,18 @@ static void debuginfo_cache__exit(void)
>  }
>  
>  
> -static int get_text_start_address(const char *exec, unsigned long *address)
> +static int get_text_start_address(const char *exec, unsigned long *address,
> +				  struct nsinfo *nsi)
>  {
>  	Elf *elf;
>  	GElf_Ehdr ehdr;
>  	GElf_Shdr shdr;
>  	int fd, ret = -ENOENT;
> +	struct nscookie nsc;
>  
> +	nsinfo__mountns_enter(nsi, &nsc);
>  	fd = open(exec, O_RDONLY);
> +	nsinfo__mountns_exit(&nsc);
>  	if (fd < 0)
>  		return -errno;
>  
> @@ -582,7 +597,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
>  			ret = -EINVAL;
>  			goto error;
>  		}
> -		ret = get_text_start_address(tp->module, &stext);
> +		ret = get_text_start_address(tp->module, &stext, NULL);
>  		if (ret < 0)
>  			goto error;
>  		addr += stext;
> @@ -659,7 +674,7 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
>  
>  	/* Prepare a map for offline binary */
>  	map = dso__new_map(pathname);
> -	if (!map || get_text_start_address(pathname, &stext) < 0) {
> +	if (!map || get_text_start_address(pathname, &stext, NULL) < 0) {
>  		pr_warning("Failed to get ELF symbols for %s\n", pathname);
>  		return -EINVAL;
>  	}
> @@ -676,7 +691,8 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
>  }
>  
>  static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
> -					  int ntevs, const char *exec)
> +					  int ntevs, const char *exec,
> +					  struct nsinfo *nsi)
>  {
>  	int i, ret = 0;
>  	unsigned long stext = 0;
> @@ -684,7 +700,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
>  	if (!exec)
>  		return 0;
>  
> -	ret = get_text_start_address(exec, &stext);
> +	ret = get_text_start_address(exec, &stext, nsi);
>  	if (ret < 0)
>  		return ret;
>  
> @@ -715,7 +731,7 @@ post_process_module_probe_trace_events(struct probe_trace_event *tevs,
>  	if (!module)
>  		return 0;
>  
> -	map = get_target_map(module, false);
> +	map = get_target_map(module, NULL, false);
>  	if (!map || debuginfo__get_text_offset(dinfo, &text_offs, true) < 0) {
>  		pr_warning("Failed to get ELF symbols for %s\n", module);
>  		return -EINVAL;
> @@ -802,7 +818,8 @@ static int post_process_probe_trace_events(struct perf_probe_event *pev,
>  	int ret;
>  
>  	if (uprobe)
> -		ret = add_exec_to_probe_trace_events(tevs, ntevs, module);
> +		ret = add_exec_to_probe_trace_events(tevs, ntevs, module,
> +						     pev->nsi);
>  	else if (module)
>  		/* Currently ref_reloc_sym based probe is not for drivers */
>  		ret = post_process_module_probe_trace_events(tevs, ntevs,
> @@ -825,7 +842,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
>  	struct debuginfo *dinfo;
>  	int ntevs, ret = 0;
>  
> -	dinfo = open_debuginfo(pev->target, !need_dwarf);
> +	dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf);
>  	if (!dinfo) {
>  		if (need_dwarf)
>  			return -ENOENT;
> @@ -945,7 +962,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
>  	char sbuf[STRERR_BUFSIZE];
>  
>  	/* Search a line range */
> -	dinfo = open_debuginfo(module, false);
> +	dinfo = open_debuginfo(module, NULL, false);
>  	if (!dinfo)
>  		return -ENOENT;
>  
> @@ -1021,14 +1038,18 @@ static int __show_line_range(struct line_range *lr, const char *module,
>  	return ret;
>  }
>  
> -int show_line_range(struct line_range *lr, const char *module, bool user)
> +int show_line_range(struct line_range *lr, const char *module,
> +		    struct nsinfo *nsi, bool user)
>  {
>  	int ret;
> +	struct nscookie nsc;
>  
>  	ret = init_probe_symbol_maps(user);
>  	if (ret < 0)
>  		return ret;
> +	nsinfo__mountns_enter(nsi, &nsc);
>  	ret = __show_line_range(lr, module, user);
> +	nsinfo__mountns_exit(&nsc);
>  	exit_probe_symbol_maps();
>  
>  	return ret;
> @@ -1111,7 +1132,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
>  	if (ret < 0)
>  		return ret;
>  
> -	dinfo = open_debuginfo(pevs->target, false);
> +	dinfo = open_debuginfo(pevs->target, pevs->nsi, false);
>  	if (!dinfo) {
>  		ret = -ENOENT;
>  		goto out;
> @@ -2703,6 +2724,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
>  	struct probe_trace_event *tev = NULL;
>  	struct probe_cache *cache = NULL;
>  	struct strlist *namelist[2] = {NULL, NULL};
> +	struct nscookie nsc;
>  
>  	up = pev->uprobes ? 1 : 0;
>  	fd[up] = __open_probe_file_and_namelist(up, &namelist[up]);
> @@ -2729,7 +2751,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
>  		if (ret < 0)
>  			break;
>  
> +		nsinfo__mountns_enter(pev->nsi, &nsc);
>  		ret = probe_file__add_event(fd[up], tev);
> +		nsinfo__mountns_exit(&nsc);
>  		if (ret < 0)
>  			break;
>  
> @@ -2805,7 +2829,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
>  	int ret, i, j, skipped = 0;
>  	char *mod_name;
>  
> -	map = get_target_map(pev->target, pev->uprobes);
> +	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
>  	if (!map) {
>  		ret = -EINVAL;
>  		goto out;
> @@ -3345,13 +3369,16 @@ int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs)
>  void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
>  {
>  	int i, j;
> +	struct perf_probe_event *pev;
>  
>  	/* Loop 3: cleanup and free trace events  */
>  	for (i = 0; i < npevs; i++) {
> +		pev = &pevs[i];
>  		for (j = 0; j < pevs[i].ntevs; j++)
>  			clear_probe_trace_event(&pevs[i].tevs[j]);
>  		zfree(&pevs[i].tevs);
>  		pevs[i].ntevs = 0;
> +		nsinfo__zput(pev->nsi);
>  		clear_perf_probe_event(&pevs[i]);
>  	}
>  }
> @@ -3409,8 +3436,8 @@ int del_perf_probe_events(struct strfilter *filter)
>  	return ret;
>  }
>  
> -int show_available_funcs(const char *target, struct strfilter *_filter,
> -					bool user)
> +int show_available_funcs(const char *target, struct nsinfo *nsi,
> +			 struct strfilter *_filter, bool user)
>  {
>          struct rb_node *nd;
>  	struct map *map;
> @@ -3421,7 +3448,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
>  		return ret;
>  
>  	/* Get a symbol map */
> -	map = get_target_map(target, user);
> +	map = get_target_map(target, nsi, user);
>  	if (!map) {
>  		pr_err("Failed to get a map for %s\n", (target) ? : "kernel");
>  		return -EINVAL;
> diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
> index 5812947..078681d 100644
> --- a/tools/perf/util/probe-event.h
> +++ b/tools/perf/util/probe-event.h
> @@ -4,6 +4,7 @@
>  #include <linux/compiler.h>
>  #include <stdbool.h>
>  #include "intlist.h"
> +#include "namespaces.h"
>  
>  /* Probe related configurations */
>  struct probe_conf {
> @@ -92,6 +93,7 @@ struct perf_probe_event {
>  	struct perf_probe_arg	*args;	/* Arguments */
>  	struct probe_trace_event *tevs;
>  	int			ntevs;
> +	struct nsinfo		*nsi;	/* Target namespace */
>  };
>  
>  /* Line range */
> @@ -163,10 +165,12 @@ int show_perf_probe_event(const char *group, const char *event,
>  			  struct perf_probe_event *pev,
>  			  const char *module, bool use_stdout);
>  int show_perf_probe_events(struct strfilter *filter);
> -int show_line_range(struct line_range *lr, const char *module, bool user);
> +int show_line_range(struct line_range *lr, const char *module,
> +		    struct nsinfo *nsi, bool user);
>  int show_available_vars(struct perf_probe_event *pevs, int npevs,
>  			struct strfilter *filter);
> -int show_available_funcs(const char *module, struct strfilter *filter, bool user);
> +int show_available_funcs(const char *module, struct nsinfo *nsi,
> +			 struct strfilter *filter, bool user);
>  void arch__fix_tev_from_maps(struct perf_probe_event *pev,
>  			     struct probe_trace_event *tev, struct map *map,
>  			     struct symbol *sym);
> @@ -180,7 +184,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...) __printf(3, 4);
>  int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
>  			    struct perf_probe_arg *pvar);
>  
> -struct map *get_target_map(const char *target, bool user);
> +struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user);
>  
>  void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
>  					   int ntevs);
> -- 
> 2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH tip/perf/core 6/7] perf documentation: updates for target-ns.
  2017-07-01  2:18 ` [PATCH tip/perf/core 6/7] perf documentation: updates for target-ns Krister Johansen
@ 2017-07-03 18:48   ` Arnaldo Carvalho de Melo
  2017-07-05 20:45     ` Krister Johansen
  0 siblings, 1 reply; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-03 18:48 UTC (permalink / raw)
  To: Krister Johansen
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel

Em Fri, Jun 30, 2017 at 07:18:58PM -0700, Krister Johansen escreveu:
> Update the perf documentation to describe the --target-ns option to
> probe and buildid-cache.  Note the existence of the new --force flag
> to top.

Can you please chop this into pieces and add each bit to the previous
patches where this option was introduced?
 
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> ---
>  tools/perf/Documentation/perf-buildid-cache.txt | 5 +++++
>  tools/perf/Documentation/perf-probe.txt         | 5 +++++
>  tools/perf/Documentation/perf-top.txt           | 4 ++++
>  3 files changed, 14 insertions(+)
> 
> diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt
> index 058064d..8468100 100644
> --- a/tools/perf/Documentation/perf-buildid-cache.txt
> +++ b/tools/perf/Documentation/perf-buildid-cache.txt
> @@ -61,6 +61,11 @@ OPTIONS
>  --verbose::
>  	Be more verbose.
>  
> +--target-ns=PID:
> +	Obtain mount namespace information from the target pid.  This is
> +	used when creating a uprobe for a process that resides in a
> +	different mount namespace from the perf(1) utility.
> +
>  SEE ALSO
>  --------
>  linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
> diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
> index 165c2b1..c1a126b 100644
> --- a/tools/perf/Documentation/perf-probe.txt
> +++ b/tools/perf/Documentation/perf-probe.txt
> @@ -130,6 +130,11 @@ OPTIONS
>  --max-probes=NUM::
>  	Set the maximum number of probe points for an event. Default is 128.
>  
> +--target-ns=PID:
> +	Obtain mount namespace information from the target pid.  This is
> +	used when creating a uprobe for a process that resides in a
> +	different mount namespace from the perf(1) utility.
> +
>  -x::
>  --exec=PATH::
>  	Specify path to the executable or shared library file for user
> diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
> index e71d638..d864ea6 100644
> --- a/tools/perf/Documentation/perf-top.txt
> +++ b/tools/perf/Documentation/perf-top.txt
> @@ -237,6 +237,10 @@ Default is to monitor all CPUS.
>  --hierarchy::
>  	Enable hierarchy output.
>  
> +--force::
> +	Don't do ownership validation.
> +
> +
>  INTERACTIVE PROMPTING KEYS
>  --------------------------
>  
> -- 
> 2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH tip/perf/core 1/7] perf symbols: find symbols in different mount namespace
  2017-07-03 18:38   ` Arnaldo Carvalho de Melo
@ 2017-07-05 20:44     ` Krister Johansen
  0 siblings, 0 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-05 20:44 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Krister Johansen, Peter Zijlstra, Ingo Molnar,
	Alexander Shishkin, linux-kernel

On Mon, Jul 03, 2017 at 03:38:27PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Fri, Jun 30, 2017 at 07:18:53PM -0700, Krister Johansen escreveu:
> > 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.
> 
> Clear implementation overall, followed tools/perf/ coding style, uses
> reference counts, etc, great! some comments below

Thanks!  Appreciate the review comments.

> > Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> > ---
> >  tools/perf/util/dso.c        |   1 +
> >  tools/perf/util/dso.h        |   2 +
> >  tools/perf/util/map.c        |   2 +
> >  tools/perf/util/namespaces.c | 127 +++++++++++++++++++++++++++++++++++++++++++
> >  tools/perf/util/namespaces.h |  33 +++++++++++
> >  tools/perf/util/symbol.c     |  11 ++++
> >  tools/perf/util/thread.c     |   3 +
> >  tools/perf/util/thread.h     |   1 +
> >  8 files changed, 180 insertions(+)
> > 
> > diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
> > index 4e7ab61..beda40e 100644
> > --- a/tools/perf/util/dso.c
> > +++ b/tools/perf/util/dso.c
> > @@ -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);
> >  }
> > diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
> > index bd061ba..78ec637 100644
> > --- a/tools/perf/util/dso.h
> > +++ b/tools/perf/util/dso.h
> > @@ -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];
> >  };
> > diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
> > index 2179b2d..5dc60ca 100644
> > --- a/tools/perf/util/map.c
> > +++ b/tools/perf/util/map.c
> > @@ -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;
> > diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
> > index 67dcbcc..d7f31e0 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)
> > +{
> > +	free(nsi->mntns_path);
> 
> 	zfree(&nsi->mntns_path);

Thanks; will fix.

> > +	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;
> > +
> > +	(void) setns(nc->oldns, CLONE_NEWNS);
> 
> We haven't been using (void) in these cases, is this to silence warnings
> about not checking the return of functions? Is this one with some
> attribute for that? But you're using even in simpler things like
> close()...
> 
> Also if we have multiple threads using this interface, wouldn't there be
> races? Humm, from 'man setns()':
> 
>        Given a file descriptor referring to a namespace, reassociate the calling thread with that namespace.
> 
> Ok, so should be ok.

Sorry, this is a leftover, still in my fingers, from when I had to
contend with code going through clint all the time.  I'll remove the
leading void casts.

The setns() call operates on a per-task basis, so it should okay.

> > +
> > +	if (nc->oldns > -1) {
> > +		(void) close(nc->oldns);
> > +		nc->oldns = -1;
> > +	}
> > +
> > +	if (nc->newns > -1) {
> > +		(void) close(nc->newns);
> > +		nc->newns = -1;
> > +	}
> > +}
> > diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
> > index 468f1e9..b20f6ea 100644
> > --- a/tools/perf/util/namespaces.h
> > +++ b/tools/perf/util/namespaces.h
> > @@ -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 */
> > diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
> > index e7a98db..60a9eaa 100644
> > --- a/tools/perf/util/symbol.c
> > +++ b/tools/perf/util/symbol.c
> > @@ -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 @@ int dso__load(struct dso *dso, struct map *map)
> >  out:
> >  	dso__set_loaded(dso, map->type);
> >  	pthread_mutex_unlock(&dso->lock);
> > +	nsinfo__mountns_exit(&nsc);
> >  
> >  	return ret;
> >  }
> > diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
> > index 378c418..aee9a42 100644
> > --- a/tools/perf/util/thread.c
> > +++ b/tools/perf/util/thread.c
> > @@ -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);
> >  }
> > diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
> > index 4eb849e..cb1a5dd 100644
> > --- a/tools/perf/util/thread.h
> > +++ b/tools/perf/util/thread.h
> > @@ -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;
> > -- 
> > 2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH tip/perf/core 3/7] perf probe: allow placing uprobes in alternate namespaces.
  2017-07-03 18:46   ` Arnaldo Carvalho de Melo
@ 2017-07-05 20:45     ` Krister Johansen
  0 siblings, 0 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-05 20:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Krister Johansen, Peter Zijlstra, Ingo Molnar,
	Alexander Shishkin, linux-kernel

On Mon, Jul 03, 2017 at 03:46:41PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Fri, Jun 30, 2017 at 07:18:55PM -0700, Krister Johansen escreveu:
> > Teaches perf how to place a uprobe on a file that's in a different mount
> > namespace.  The user must add the probe using the --target-ns argument
> > to perf probe.  Once it has been placed, it may be recorded against
> > without further namespace-specific commands.
> > 
> > Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> > ---
> >  tools/perf/builtin-probe.c    | 44 ++++++++++++++++++++++--
> >  tools/perf/util/probe-event.c | 79 +++++++++++++++++++++++++++++--------------
> >  tools/perf/util/probe-event.h | 10 ++++--
> >  3 files changed, 101 insertions(+), 32 deletions(-)
> > 
> > diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
> > index cf9f9e9..5ab2e00 100644
> > --- a/tools/perf/builtin-probe.c
> > +++ b/tools/perf/builtin-probe.c
> > @@ -58,6 +58,7 @@ static struct {
> >  	struct line_range line_range;
> >  	char *target;
> >  	struct strfilter *filter;
> > +	struct nsinfo *nsi;
> >  } params;
> >  
> >  /* Parse an event definition. Note that any error must die. */
> > @@ -80,6 +81,9 @@ static int parse_probe_event(const char *str)
> >  		params.target_used = true;
> >  	}
> >  
> > +	if (params.nsi)
> > +		pev->nsi = nsinfo__get(params.nsi);
> > +
> >  	/* Parse a perf-probe command into event */
> >  	ret = parse_perf_probe_command(str, pev);
> >  	pr_debug("%d arguments\n", pev->nargs);
> > @@ -178,6 +182,7 @@ static int opt_set_target(const struct option *opt, const char *str,
> >  {
> >  	int ret = -ENOENT;
> >  	char *tmp;
> > +	struct nscookie nsc;
> >  
> >  	if  (str) {
> >  		if (!strcmp(opt->long_name, "exec"))
> > @@ -189,7 +194,9 @@ static int opt_set_target(const struct option *opt, const char *str,
> >  
> >  		/* Expand given path to absolute path, except for modulename */
> >  		if (params.uprobes || strchr(str, '/')) {
> > +			nsinfo__mountns_enter(params.nsi, &nsc);
> >  			tmp = realpath(str, NULL);
> > +			nsinfo__mountns_exit(&nsc);
> 
> Perhaps have a nsinfo__realpath()? Don't know if this will be used
> elsewhere, but looks shorter.

Sure.  I think I do this in a few different places.  I'd be happy to
pull this into its own function and re-use instead.

> >  			if (!tmp) {
> >  				pr_warning("Failed to get the absolute path of %s: %m\n", str);
> >  				return ret;
> > @@ -208,6 +215,34 @@ static int opt_set_target(const struct option *opt, const char *str,
> >  	return ret;
> >  }
> >  
> > +static int opt_set_target_ns(const struct option *opt __maybe_unused,
> > +			     const char *str, int unset __maybe_unused)
> > +{
> > +	int ret = -ENOENT;
> > +	pid_t ns_pid;
> > +	struct nsinfo *nsip;
> > +
> > +	if (str) {
> > +		errno = 0;
> > +		ns_pid = (pid_t)strtol(str, NULL, 10);
> > +		if (errno != 0) {
> > +			ret = -errno;
> > +			pr_warning("Failed to parse %s as a pid: %s\n", str,
> > +				   strerror(errno));
> > +			return ret;
> > +		}
> > +		nsip = nsinfo__new(ns_pid);
> > +		if (nsip && nsip->need_setns)
> > +			params.nsi = nsinfo__get(nsip);
> > +		nsinfo__put(nsip);
> > +
> > +		ret = 0;
> > +	}
> > +
> > +	return ret;
> > +}
> > +
> > +
> >  /* Command option callbacks */
> >  
> >  #ifdef HAVE_DWARF_SUPPORT
> > @@ -299,6 +334,7 @@ static void cleanup_params(void)
> >  	line_range__clear(&params.line_range);
> >  	free(params.target);
> >  	strfilter__delete(params.filter);
> > +	nsinfo__put(params.nsi);
> >  	memset(&params, 0, sizeof(params));
> >  }
> >  
> > @@ -554,6 +590,8 @@ __cmd_probe(int argc, const char **argv)
> >  	OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
> >  	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
> >  		   "Look for files with symbols relative to this directory"),
> > +	OPT_CALLBACK(0, "target-ns", NULL, "pid",
> > +		     "target pid for namespace information", opt_set_target_ns),
> >  	OPT_END()
> >  	};
> >  	int ret;
> > @@ -634,15 +672,15 @@ __cmd_probe(int argc, const char **argv)
> >  			pr_err_with_code("  Error: Failed to show event list.", ret);
> >  		return ret;
> >  	case 'F':
> > -		ret = show_available_funcs(params.target, params.filter,
> > -					params.uprobes);
> > +		ret = show_available_funcs(params.target, params.nsi,
> > +					   params.filter, params.uprobes);
> >  		if (ret < 0)
> >  			pr_err_with_code("  Error: Failed to show functions.", ret);
> >  		return ret;
> >  #ifdef HAVE_DWARF_SUPPORT
> >  	case 'L':
> >  		ret = show_line_range(&params.line_range, params.target,
> > -				      params.uprobes);
> > +				      params.nsi, params.uprobes);
> >  		if (ret < 0)
> >  			pr_err_with_code("  Error: Failed to show lines.", ret);
> >  		return ret;
> > diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
> > index 84e7e69..dce4f12 100644
> > --- a/tools/perf/util/probe-event.c
> > +++ b/tools/perf/util/probe-event.c
> > @@ -184,13 +184,19 @@ static struct map *kernel_get_module_map(const char *module)
> >  	return NULL;
> >  }
> >  
> > -struct map *get_target_map(const char *target, bool user)
> > +struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user)
> >  {
> >  	/* Init maps of given executable or kernel */
> > -	if (user)
> > -		return dso__new_map(target);
> > -	else
> > +	if (user) {
> > +		struct map *map;
> > +
> > +		map = dso__new_map(target);
> > +		if (map && map->dso)
> > +			map->dso->nsinfo = nsinfo__get(nsi);
> > +		return map;
> > +	} else {
> >  		return kernel_get_module_map(target);
> > +	}
> >  }
> >  
> >  static int convert_exec_to_group(const char *exec, char **result)
> > @@ -366,7 +372,8 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
> >  static int find_alternative_probe_point(struct debuginfo *dinfo,
> >  					struct perf_probe_point *pp,
> >  					struct perf_probe_point *result,
> > -					const char *target, bool uprobes)
> > +					const char *target, struct nsinfo *nsi,
> > +					bool uprobes)
> >  {
> >  	struct map *map = NULL;
> >  	struct symbol *sym;
> > @@ -377,7 +384,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
> >  	if (!pp->function || pp->file)
> >  		return -ENOTSUP;
> >  
> > -	map = get_target_map(target, uprobes);
> > +	map = get_target_map(target, nsi, uprobes);
> >  	if (!map)
> >  		return -EINVAL;
> >  
> > @@ -421,8 +428,8 @@ static int get_alternative_probe_event(struct debuginfo *dinfo,
> >  
> >  	memcpy(tmp, &pev->point, sizeof(*tmp));
> >  	memset(&pev->point, 0, sizeof(pev->point));
> > -	ret = find_alternative_probe_point(dinfo, tmp, &pev->point,
> > -					   pev->target, pev->uprobes);
> > +	ret = find_alternative_probe_point(dinfo, tmp, &pev->point, pev->target,
> > +					   pev->nsi, pev->uprobes);
> >  	if (ret < 0)
> >  		memcpy(&pev->point, tmp, sizeof(*tmp));
> >  
> > @@ -444,7 +451,7 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
> >  	if (lr->end != INT_MAX)
> >  		len = lr->end - lr->start;
> >  	ret = find_alternative_probe_point(dinfo, &pp, &result,
> > -					   target, user);
> > +					   target, NULL, user);
> >  	if (!ret) {
> >  		lr->function = result.function;
> >  		lr->file = result.file;
> > @@ -457,12 +464,14 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
> >  }
> >  
> >  /* Open new debuginfo of given module */
> > -static struct debuginfo *open_debuginfo(const char *module, bool silent)
> > +static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
> > +					bool silent)
> >  {
> >  	const char *path = module;
> >  	char reason[STRERR_BUFSIZE];
> >  	struct debuginfo *ret = NULL;
> >  	struct dso *dso = NULL;
> > +	struct nscookie nsc;
> >  	int err;
> >  
> >  	if (!module || !strchr(module, '/')) {
> > @@ -480,6 +489,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
> >  		}
> >  		path = dso->long_name;
> >  	}
> > +	nsinfo__mountns_enter(nsi, &nsc);
> >  	ret = debuginfo__new(path);
> >  	if (!ret && !silent) {
> >  		pr_warning("The %s file has no debug information.\n", path);
> > @@ -489,6 +499,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
> >  			pr_warning("Rebuild with -g, ");
> >  		pr_warning("or install an appropriate debuginfo package.\n");
> >  	}
> > +	nsinfo__mountns_exit(&nsc);
> >  	return ret;
> >  }
> >  
> > @@ -516,7 +527,7 @@ static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
> >  		goto out;
> >  	}
> >  
> > -	debuginfo_cache = open_debuginfo(module, silent);
> > +	debuginfo_cache = open_debuginfo(module, NULL, silent);
> >  	if (!debuginfo_cache)
> >  		zfree(&debuginfo_cache_path);
> >  out:
> > @@ -531,14 +542,18 @@ static void debuginfo_cache__exit(void)
> >  }
> >  
> >  
> > -static int get_text_start_address(const char *exec, unsigned long *address)
> > +static int get_text_start_address(const char *exec, unsigned long *address,
> > +				  struct nsinfo *nsi)
> >  {
> >  	Elf *elf;
> >  	GElf_Ehdr ehdr;
> >  	GElf_Shdr shdr;
> >  	int fd, ret = -ENOENT;
> > +	struct nscookie nsc;
> >  
> > +	nsinfo__mountns_enter(nsi, &nsc);
> >  	fd = open(exec, O_RDONLY);
> > +	nsinfo__mountns_exit(&nsc);
> >  	if (fd < 0)
> >  		return -errno;
> >  
> > @@ -582,7 +597,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
> >  			ret = -EINVAL;
> >  			goto error;
> >  		}
> > -		ret = get_text_start_address(tp->module, &stext);
> > +		ret = get_text_start_address(tp->module, &stext, NULL);
> >  		if (ret < 0)
> >  			goto error;
> >  		addr += stext;
> > @@ -659,7 +674,7 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
> >  
> >  	/* Prepare a map for offline binary */
> >  	map = dso__new_map(pathname);
> > -	if (!map || get_text_start_address(pathname, &stext) < 0) {
> > +	if (!map || get_text_start_address(pathname, &stext, NULL) < 0) {
> >  		pr_warning("Failed to get ELF symbols for %s\n", pathname);
> >  		return -EINVAL;
> >  	}
> > @@ -676,7 +691,8 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
> >  }
> >  
> >  static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
> > -					  int ntevs, const char *exec)
> > +					  int ntevs, const char *exec,
> > +					  struct nsinfo *nsi)
> >  {
> >  	int i, ret = 0;
> >  	unsigned long stext = 0;
> > @@ -684,7 +700,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
> >  	if (!exec)
> >  		return 0;
> >  
> > -	ret = get_text_start_address(exec, &stext);
> > +	ret = get_text_start_address(exec, &stext, nsi);
> >  	if (ret < 0)
> >  		return ret;
> >  
> > @@ -715,7 +731,7 @@ post_process_module_probe_trace_events(struct probe_trace_event *tevs,
> >  	if (!module)
> >  		return 0;
> >  
> > -	map = get_target_map(module, false);
> > +	map = get_target_map(module, NULL, false);
> >  	if (!map || debuginfo__get_text_offset(dinfo, &text_offs, true) < 0) {
> >  		pr_warning("Failed to get ELF symbols for %s\n", module);
> >  		return -EINVAL;
> > @@ -802,7 +818,8 @@ static int post_process_probe_trace_events(struct perf_probe_event *pev,
> >  	int ret;
> >  
> >  	if (uprobe)
> > -		ret = add_exec_to_probe_trace_events(tevs, ntevs, module);
> > +		ret = add_exec_to_probe_trace_events(tevs, ntevs, module,
> > +						     pev->nsi);
> >  	else if (module)
> >  		/* Currently ref_reloc_sym based probe is not for drivers */
> >  		ret = post_process_module_probe_trace_events(tevs, ntevs,
> > @@ -825,7 +842,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
> >  	struct debuginfo *dinfo;
> >  	int ntevs, ret = 0;
> >  
> > -	dinfo = open_debuginfo(pev->target, !need_dwarf);
> > +	dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf);
> >  	if (!dinfo) {
> >  		if (need_dwarf)
> >  			return -ENOENT;
> > @@ -945,7 +962,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
> >  	char sbuf[STRERR_BUFSIZE];
> >  
> >  	/* Search a line range */
> > -	dinfo = open_debuginfo(module, false);
> > +	dinfo = open_debuginfo(module, NULL, false);
> >  	if (!dinfo)
> >  		return -ENOENT;
> >  
> > @@ -1021,14 +1038,18 @@ static int __show_line_range(struct line_range *lr, const char *module,
> >  	return ret;
> >  }
> >  
> > -int show_line_range(struct line_range *lr, const char *module, bool user)
> > +int show_line_range(struct line_range *lr, const char *module,
> > +		    struct nsinfo *nsi, bool user)
> >  {
> >  	int ret;
> > +	struct nscookie nsc;
> >  
> >  	ret = init_probe_symbol_maps(user);
> >  	if (ret < 0)
> >  		return ret;
> > +	nsinfo__mountns_enter(nsi, &nsc);
> >  	ret = __show_line_range(lr, module, user);
> > +	nsinfo__mountns_exit(&nsc);
> >  	exit_probe_symbol_maps();
> >  
> >  	return ret;
> > @@ -1111,7 +1132,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
> >  	if (ret < 0)
> >  		return ret;
> >  
> > -	dinfo = open_debuginfo(pevs->target, false);
> > +	dinfo = open_debuginfo(pevs->target, pevs->nsi, false);
> >  	if (!dinfo) {
> >  		ret = -ENOENT;
> >  		goto out;
> > @@ -2703,6 +2724,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
> >  	struct probe_trace_event *tev = NULL;
> >  	struct probe_cache *cache = NULL;
> >  	struct strlist *namelist[2] = {NULL, NULL};
> > +	struct nscookie nsc;
> >  
> >  	up = pev->uprobes ? 1 : 0;
> >  	fd[up] = __open_probe_file_and_namelist(up, &namelist[up]);
> > @@ -2729,7 +2751,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
> >  		if (ret < 0)
> >  			break;
> >  
> > +		nsinfo__mountns_enter(pev->nsi, &nsc);
> >  		ret = probe_file__add_event(fd[up], tev);
> > +		nsinfo__mountns_exit(&nsc);
> >  		if (ret < 0)
> >  			break;
> >  
> > @@ -2805,7 +2829,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
> >  	int ret, i, j, skipped = 0;
> >  	char *mod_name;
> >  
> > -	map = get_target_map(pev->target, pev->uprobes);
> > +	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
> >  	if (!map) {
> >  		ret = -EINVAL;
> >  		goto out;
> > @@ -3345,13 +3369,16 @@ int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs)
> >  void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
> >  {
> >  	int i, j;
> > +	struct perf_probe_event *pev;
> >  
> >  	/* Loop 3: cleanup and free trace events  */
> >  	for (i = 0; i < npevs; i++) {
> > +		pev = &pevs[i];
> >  		for (j = 0; j < pevs[i].ntevs; j++)
> >  			clear_probe_trace_event(&pevs[i].tevs[j]);
> >  		zfree(&pevs[i].tevs);
> >  		pevs[i].ntevs = 0;
> > +		nsinfo__zput(pev->nsi);
> >  		clear_perf_probe_event(&pevs[i]);
> >  	}
> >  }
> > @@ -3409,8 +3436,8 @@ int del_perf_probe_events(struct strfilter *filter)
> >  	return ret;
> >  }
> >  
> > -int show_available_funcs(const char *target, struct strfilter *_filter,
> > -					bool user)
> > +int show_available_funcs(const char *target, struct nsinfo *nsi,
> > +			 struct strfilter *_filter, bool user)
> >  {
> >          struct rb_node *nd;
> >  	struct map *map;
> > @@ -3421,7 +3448,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
> >  		return ret;
> >  
> >  	/* Get a symbol map */
> > -	map = get_target_map(target, user);
> > +	map = get_target_map(target, nsi, user);
> >  	if (!map) {
> >  		pr_err("Failed to get a map for %s\n", (target) ? : "kernel");
> >  		return -EINVAL;
> > diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
> > index 5812947..078681d 100644
> > --- a/tools/perf/util/probe-event.h
> > +++ b/tools/perf/util/probe-event.h
> > @@ -4,6 +4,7 @@
> >  #include <linux/compiler.h>
> >  #include <stdbool.h>
> >  #include "intlist.h"
> > +#include "namespaces.h"
> >  
> >  /* Probe related configurations */
> >  struct probe_conf {
> > @@ -92,6 +93,7 @@ struct perf_probe_event {
> >  	struct perf_probe_arg	*args;	/* Arguments */
> >  	struct probe_trace_event *tevs;
> >  	int			ntevs;
> > +	struct nsinfo		*nsi;	/* Target namespace */
> >  };
> >  
> >  /* Line range */
> > @@ -163,10 +165,12 @@ int show_perf_probe_event(const char *group, const char *event,
> >  			  struct perf_probe_event *pev,
> >  			  const char *module, bool use_stdout);
> >  int show_perf_probe_events(struct strfilter *filter);
> > -int show_line_range(struct line_range *lr, const char *module, bool user);
> > +int show_line_range(struct line_range *lr, const char *module,
> > +		    struct nsinfo *nsi, bool user);
> >  int show_available_vars(struct perf_probe_event *pevs, int npevs,
> >  			struct strfilter *filter);
> > -int show_available_funcs(const char *module, struct strfilter *filter, bool user);
> > +int show_available_funcs(const char *module, struct nsinfo *nsi,
> > +			 struct strfilter *filter, bool user);
> >  void arch__fix_tev_from_maps(struct perf_probe_event *pev,
> >  			     struct probe_trace_event *tev, struct map *map,
> >  			     struct symbol *sym);
> > @@ -180,7 +184,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...) __printf(3, 4);
> >  int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
> >  			    struct perf_probe_arg *pvar);
> >  
> > -struct map *get_target_map(const char *target, bool user);
> > +struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user);
> >  
> >  void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
> >  					   int ntevs);
> > -- 
> > 2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH tip/perf/core 6/7] perf documentation: updates for target-ns.
  2017-07-03 18:48   ` Arnaldo Carvalho de Melo
@ 2017-07-05 20:45     ` Krister Johansen
  2017-07-06  1:48       ` [PATCH v2 tip/perf/core 0/6] namespace tracing improvements Krister Johansen
  0 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-05 20:45 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Krister Johansen, Peter Zijlstra, Ingo Molnar,
	Alexander Shishkin, linux-kernel

On Mon, Jul 03, 2017 at 03:48:36PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Fri, Jun 30, 2017 at 07:18:58PM -0700, Krister Johansen escreveu:
> > Update the perf documentation to describe the --target-ns option to
> > probe and buildid-cache.  Note the existence of the new --force flag
> > to top.
> 
> Can you please chop this into pieces and add each bit to the previous
> patches where this option was introduced?

Yes; will do.  Once I have, I'll send out an updated version of the
patchset that includes this and the other changes you've requested.

> > Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> > ---
> >  tools/perf/Documentation/perf-buildid-cache.txt | 5 +++++
> >  tools/perf/Documentation/perf-probe.txt         | 5 +++++
> >  tools/perf/Documentation/perf-top.txt           | 4 ++++
> >  3 files changed, 14 insertions(+)
> > 
> > diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt
> > index 058064d..8468100 100644
> > --- a/tools/perf/Documentation/perf-buildid-cache.txt
> > +++ b/tools/perf/Documentation/perf-buildid-cache.txt
> > @@ -61,6 +61,11 @@ OPTIONS
> >  --verbose::
> >  	Be more verbose.
> >  
> > +--target-ns=PID:
> > +	Obtain mount namespace information from the target pid.  This is
> > +	used when creating a uprobe for a process that resides in a
> > +	different mount namespace from the perf(1) utility.
> > +
> >  SEE ALSO
> >  --------
> >  linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
> > diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
> > index 165c2b1..c1a126b 100644
> > --- a/tools/perf/Documentation/perf-probe.txt
> > +++ b/tools/perf/Documentation/perf-probe.txt
> > @@ -130,6 +130,11 @@ OPTIONS
> >  --max-probes=NUM::
> >  	Set the maximum number of probe points for an event. Default is 128.
> >  
> > +--target-ns=PID:
> > +	Obtain mount namespace information from the target pid.  This is
> > +	used when creating a uprobe for a process that resides in a
> > +	different mount namespace from the perf(1) utility.
> > +
> >  -x::
> >  --exec=PATH::
> >  	Specify path to the executable or shared library file for user
> > diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
> > index e71d638..d864ea6 100644
> > --- a/tools/perf/Documentation/perf-top.txt
> > +++ b/tools/perf/Documentation/perf-top.txt
> > @@ -237,6 +237,10 @@ Default is to monitor all CPUS.
> >  --hierarchy::
> >  	Enable hierarchy output.
> >  
> > +--force::
> > +	Don't do ownership validation.
> > +
> > +
> >  INTERACTIVE PROMPTING KEYS
> >  --------------------------
> >  
> > -- 
> > 2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v2 tip/perf/core 0/6] namespace tracing improvements
  2017-07-05 20:45     ` Krister Johansen
@ 2017-07-06  1:48       ` Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace Krister Johansen
                           ` (5 more replies)
  0 siblings, 6 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-06  1:48 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Brendan Gregg
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel,
	Thomas-Mich Richter, Krister Johansen

This patch set is similar to a set of features I implemented for bcc
back in April.  At a high-level it does the following:

- Allow the tracing tools to resolve symbols if the traced process has a
  binary that is in a different mount namespace from perf.

- Allow the perf-<pid>.map files to live either in /tmp in the target
  process' mount namespace, or the tracing process's /tmp in its own
  mount namespace.

- Adds the ability to set and record against uprobes and USDT probes
  when the target process resides in a different mount namespace.

With these changes I can get pretty good insight into what containerized
processes on my systems are doing.  In addition to the above, I also
modified the builid cache code to cache relevant bits from the target
namespaces so that it's possible to preserve more information should a
container exit and take the mounted volumes along with it.

-K

Changes v1 -> v2:

- stylistic cleanups in namespace.c (Arnaldo)
- zfree instead of free in nsinfo__delete (Arnaldo)
- un-reflow map__new and dso__load (Arnaldo)
- introduce nsinfo__realpath (Arnaldo)
- move documentation into the patch that introduced the change. (Arnaldo)
- update help descriptions (Brendan)
- add perf-probe examples for uprobes and USDT (Brendan)


Krister Johansen (6):
  perf symbols: find symbols in different mount namespace
  perf maps: lookup maps in both intitial mountns and inner mountns.
  perf probe: allow placing uprobes in alternate namespaces.
  perf buildid-cache: support binary objects from other namespaces
  perf top: support lookup of symbols in other mount namespaces.
  perf buildid-cache: cache debuginfo

 tools/perf/Documentation/perf-buildid-cache.txt |   5 +
 tools/perf/Documentation/perf-probe.txt         |  14 ++
 tools/perf/Documentation/perf-top.txt           |   4 +
 tools/perf/builtin-buildid-cache.c              |  54 ++++--
 tools/perf/builtin-probe.c                      |  45 ++++-
 tools/perf/builtin-top.c                        |  15 ++
 tools/perf/tests/sdt.c                          |   4 +-
 tools/perf/util/annotate.c                      |   2 +-
 tools/perf/util/build-id.c                      | 119 ++++++++++---
 tools/perf/util/build-id.h                      |  12 +-
 tools/perf/util/dso.c                           |  21 ++-
 tools/perf/util/dso.h                           |   3 +
 tools/perf/util/machine.c                       |   7 +-
 tools/perf/util/map.c                           |  23 ++-
 tools/perf/util/map.h                           |   2 +-
 tools/perf/util/namespaces.c                    | 211 ++++++++++++++++++++++++
 tools/perf/util/namespaces.h                    |  38 +++++
 tools/perf/util/parse-events.c                  |   2 +-
 tools/perf/util/probe-event.c                   |  85 ++++++----
 tools/perf/util/probe-event.h                   |  10 +-
 tools/perf/util/probe-file.c                    |  19 ++-
 tools/perf/util/probe-file.h                    |   4 +-
 tools/perf/util/symbol.c                        |  92 +++++++++--
 tools/perf/util/thread.c                        |   3 +
 tools/perf/util/thread.h                        |   1 +
 tools/perf/util/util.c                          |  34 +++-
 tools/perf/util/util.h                          |   2 +
 27 files changed, 713 insertions(+), 118 deletions(-)

-- 
2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-06  1:48       ` [PATCH v2 tip/perf/core 0/6] namespace tracing improvements Krister Johansen
@ 2017-07-06  1:48         ` Krister Johansen
  2017-07-06 19:41           ` Arnaldo Carvalho de Melo
  2017-07-20  8:48           ` [tip:perf/core] perf symbols: Find " tip-bot for Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 2/6] perf maps: lookup maps in both intitial mountns and inner mountns Krister Johansen
                           ` (4 subsequent siblings)
  5 siblings, 2 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-06  1:48 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Brendan Gregg
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel,
	Thomas-Mich Richter, Krister Johansen

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>
---
 tools/perf/util/dso.c        |   1 +
 tools/perf/util/dso.h        |   2 +
 tools/perf/util/map.c        |   2 +
 tools/perf/util/namespaces.c | 127 +++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/namespaces.h |  33 +++++++++++
 tools/perf/util/symbol.c     |  11 ++++
 tools/perf/util/thread.c     |   3 +
 tools/perf/util/thread.h     |   1 +
 8 files changed, 180 insertions(+)

diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 4e7ab61..beda40e 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -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);
 }
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index bd061ba..78ec637 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -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];
 };
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 2179b2d..5dc60ca 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -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;
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index 67dcbcc..bcc6bb1 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;
+	}
+}
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 468f1e9..b20f6ea 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -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 */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e7a98db..60a9eaa 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -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 @@ int dso__load(struct dso *dso, struct map *map)
 out:
 	dso__set_loaded(dso, map->type);
 	pthread_mutex_unlock(&dso->lock);
+	nsinfo__mountns_exit(&nsc);
 
 	return ret;
 }
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 378c418..aee9a42 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -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);
 }
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 4eb849e..cb1a5dd 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -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;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 tip/perf/core 2/6] perf maps: lookup maps in both intitial mountns and inner mountns.
  2017-07-06  1:48       ` [PATCH v2 tip/perf/core 0/6] namespace tracing improvements Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace Krister Johansen
@ 2017-07-06  1:48         ` Krister Johansen
  2017-07-20  8:48           ` [tip:perf/core] perf maps: Lookup " tip-bot for Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 3/6] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
                           ` (3 subsequent siblings)
  5 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-06  1:48 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Brendan Gregg
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel,
	Thomas-Mich Richter, Krister Johansen

If a process is in a mountns and has symbols in /tmp/perf-<pid>.map,
look first in the namespace using the tgid for the pidns that the
process might be in.  If the map isn't found there, try looking in
the mountns where perf is running, and use the tgid that's appropriate
for perf's pid namespace.  If all else fails, use the original pid.

This allows us to locate a symbol map file in the mount namespace, if
it was generated there.  However, we also try the tool's /tmp in case
it's there instead.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/util/machine.c    |  4 +--
 tools/perf/util/map.c        | 23 +++++++++---
 tools/perf/util/map.h        |  2 +-
 tools/perf/util/namespaces.c | 83 ++++++++++++++++++++++++++++++++++++++++----
 tools/perf/util/namespaces.h |  5 ++-
 tools/perf/util/symbol.c     | 70 ++++++++++++++++++++++++++++++-------
 6 files changed, 160 insertions(+), 27 deletions(-)

diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index d7f31cb..4b0f16c 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1390,7 +1390,7 @@ int machine__process_mmap2_event(struct machine *machine,
 
 	map = map__new(machine, event->mmap2.start,
 			event->mmap2.len, event->mmap2.pgoff,
-			event->mmap2.pid, event->mmap2.maj,
+			event->mmap2.maj,
 			event->mmap2.min, event->mmap2.ino,
 			event->mmap2.ino_generation,
 			event->mmap2.prot,
@@ -1448,7 +1448,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
 
 	map = map__new(machine, event->mmap.start,
 			event->mmap.len, event->mmap.pgoff,
-			event->mmap.pid, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0,
 			event->mmap.filename,
 			type, thread);
 
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 5dc60ca..bdaa0a4 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -146,11 +146,13 @@ void map__init(struct map *map, enum map_type type,
 }
 
 struct map *map__new(struct machine *machine, u64 start, u64 len,
-		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
+		     u64 pgoff, u32 d_maj, u32 d_min, u64 ino,
 		     u64 ino_gen, u32 prot, u32 flags, char *filename,
 		     enum map_type type, struct thread *thread)
 {
 	struct map *map = malloc(sizeof(*map));
+	struct nsinfo *nsi = NULL;
+	struct nsinfo *nnsi;
 
 	if (map != NULL) {
 		char newfilename[PATH_MAX];
@@ -168,9 +170,11 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
 		map->ino_generation = ino_gen;
 		map->prot = prot;
 		map->flags = flags;
+		nsi = nsinfo__get(thread->nsinfo);
 
-		if ((anon || no_dso) && type == MAP__FUNCTION) {
-			snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
+		if ((anon || no_dso) && nsi && type == MAP__FUNCTION) {
+			snprintf(newfilename, sizeof(newfilename),
+				 "/tmp/perf-%d.map", nsi->pid);
 			filename = newfilename;
 		}
 
@@ -180,6 +184,16 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
 		}
 
 		if (vdso) {
+			/* The vdso maps are always on the host and not the
+			 * container.  Ensure that we don't use setns to look
+			 * them up.
+			 */
+			nnsi = nsinfo__copy(nsi);
+			if (nnsi) {
+				nsinfo__put(nsi);
+				nnsi->need_setns = false;
+				nsi = nnsi;
+			}
 			pgoff = 0;
 			dso = machine__findnew_vdso(machine, thread);
 		} else
@@ -201,11 +215,12 @@ 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->nsinfo = nsi;
 		dso__put(dso);
 	}
 	return map;
 out_delete:
+	nsinfo__put(nsi);
 	free(map);
 	return NULL;
 }
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f9e8ac8..73aacf7 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -141,7 +141,7 @@ struct thread;
 void map__init(struct map *map, enum map_type type,
 	       u64 start, u64 end, u64 pgoff, struct dso *dso);
 struct map *map__new(struct machine *machine, u64 start, u64 len,
-		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
+		     u64 pgoff, u32 d_maj, u32 d_min, u64 ino,
 		     u64 ino_gen, u32 prot, u32 flags,
 		     char *filename, enum map_type type, struct thread *thread);
 struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index bcc6bb1..fc5f398 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -40,18 +40,23 @@ void namespaces__free(struct namespaces *namespaces)
 	free(namespaces);
 }
 
-void nsinfo__init(struct nsinfo *nsi)
+int nsinfo__init(struct nsinfo *nsi)
 {
 	char oldns[PATH_MAX];
+	char spath[PATH_MAX];
 	char *newns = NULL;
+	char *statln = NULL;
 	struct stat old_stat;
 	struct stat new_stat;
+	FILE *f = NULL;
+	size_t linesz = 0;
+	int rv = -1;
 
 	if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
-		return;
+		return rv;
 
 	if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1)
-		return;
+		return rv;
 
 	if (stat(oldns, &old_stat) < 0)
 		goto out;
@@ -68,24 +73,89 @@ void nsinfo__init(struct nsinfo *nsi)
 		newns = NULL;
 	}
 
+	/* If we're dealing with a process that is in a different PID namespace,
+	 * attempt to work out the innermost tgid for the process.
+	 */
+	if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsi->pid) >= PATH_MAX)
+		goto out;
+
+	f = fopen(spath, "r");
+	if (f == NULL)
+		goto out;
+
+	while (getline(&statln, &linesz, f) != -1) {
+		/* Use tgid if CONFIG_PID_NS is not defined. */
+		if (strstr(statln, "Tgid:") != NULL) {
+			nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'),
+						     NULL, 10);
+			nsi->nstgid = nsi->tgid;
+		}
+
+		if (strstr(statln, "NStgid:") != NULL) {
+			nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'),
+						     NULL, 10);
+			break;
+		}
+	}
+	rv = 0;
+
 out:
+	if (f != NULL)
+		(void) fclose(f);
+	free(statln);
 	free(newns);
+	return rv;
 }
 
 struct nsinfo *nsinfo__new(pid_t pid)
 {
-	struct nsinfo *nsi = calloc(1, sizeof(*nsi));
+	struct nsinfo *nsi;
 
+	if (pid == 0)
+		return NULL;
+
+	nsi = calloc(1, sizeof(*nsi));
 	if (nsi != NULL) {
 		nsi->pid = pid;
+		nsi->tgid = pid;
+		nsi->nstgid = pid;
 		nsi->need_setns = false;
-		nsinfo__init(nsi);
+		/* Init may fail if the process exits while we're trying to look
+		 * at its proc information.  In that case, save the pid but
+		 * don't try to enter the namespace.
+		 */
+		if (nsinfo__init(nsi) == -1)
+			nsi->need_setns = false;
+
 		refcount_set(&nsi->refcnt, 1);
 	}
 
 	return nsi;
 }
 
+struct nsinfo *nsinfo__copy(struct nsinfo *nsi)
+{
+	struct nsinfo *nnsi;
+
+	nnsi = calloc(1, sizeof(*nnsi));
+	if (nnsi != NULL) {
+		nnsi->pid = nsi->pid;
+		nnsi->tgid = nsi->tgid;
+		nnsi->nstgid = nsi->nstgid;
+		nnsi->need_setns = nsi->need_setns;
+		if (nsi->mntns_path) {
+			nnsi->mntns_path = strdup(nsi->mntns_path);
+			if (!nnsi->mntns_path) {
+				free(nnsi);
+				return NULL;
+			}
+		}
+		refcount_set(&nnsi->refcnt, 1);
+	}
+
+	return nnsi;
+}
+
 void nsinfo__delete(struct nsinfo *nsi)
 {
 	zfree(&nsi->mntns_path);
@@ -105,7 +175,8 @@ void nsinfo__put(struct nsinfo *nsi)
 		nsinfo__delete(nsi);
 }
 
-void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc)
+void nsinfo__mountns_enter(struct nsinfo *nsi,
+				  struct nscookie *nc)
 {
 	char curpath[PATH_MAX];
 	int oldns = -1;
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index b20f6ea..f19aa41 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -26,6 +26,8 @@ void namespaces__free(struct namespaces *namespaces);
 
 struct nsinfo {
 	pid_t			pid;
+	pid_t			tgid;
+	pid_t			nstgid;
 	bool			need_setns;
 	char			*mntns_path;
 	refcount_t		refcnt;
@@ -36,8 +38,9 @@ struct nscookie {
 	int			newns;
 };
 
-void nsinfo__init(struct nsinfo *nsi);
+int nsinfo__init(struct nsinfo *nsi);
 struct nsinfo *nsinfo__new(pid_t pid);
+struct nsinfo *nsinfo__copy(struct nsinfo *nsi);
 void nsinfo__delete(struct nsinfo *nsi);
 
 struct nsinfo *nsinfo__get(struct nsinfo *nsi);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 60a9eaa..21c97cc 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -19,7 +19,6 @@
 #include "strlist.h"
 #include "intlist.h"
 #include "namespaces.h"
-#include "vdso.h"
 #include "header.h"
 #include "path.h"
 #include "sane_ctype.h"
@@ -1327,14 +1326,15 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
 	return __dso__load_kallsyms(dso, filename, map, false);
 }
 
-static int dso__load_perf_map(struct dso *dso, struct map *map)
+static int dso__load_perf_map(const char *map_path, struct dso *dso,
+			      struct map *map)
 {
 	char *line = NULL;
 	size_t n;
 	FILE *file;
 	int nr_syms = 0;
 
-	file = fopen(dso->long_name, "r");
+	file = fopen(map_path, "r");
 	if (file == NULL)
 		goto out_failure;
 
@@ -1426,6 +1426,45 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
 	}
 }
 
+/* Checks for the existence of the perf-<pid>.map file in two different
+ * locations.  First, if the process is a separate mount namespace, check in
+ * that namespace using the pid of the innermost pid namespace.  If's not in a
+ * namespace, or the file can't be found there, try in the mount namespace of
+ * the tracing process using our view of its pid.
+ */
+static int dso__find_perf_map(char *filebuf, size_t bufsz,
+			      struct nsinfo **nsip)
+{
+	struct nscookie nsc;
+	struct nsinfo *nsi;
+	struct nsinfo *nnsi;
+	int rc = -1;
+
+	nsi = *nsip;
+
+	if (nsi->need_setns) {
+		snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nsi->nstgid);
+		nsinfo__mountns_enter(nsi, &nsc);
+		rc = access(filebuf, R_OK);
+		nsinfo__mountns_exit(&nsc);
+		if (rc == 0)
+			return rc;
+	}
+
+	nnsi = nsinfo__copy(nsi);
+	if (nnsi) {
+		nsinfo__put(nsi);
+
+		nnsi->need_setns = false;
+		snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nnsi->tgid);
+		*nsip = nnsi;
+		rc = 0;
+	}
+
+	return rc;
+}
+
+
 int dso__load(struct dso *dso, struct map *map)
 {
 	char *name;
@@ -1437,18 +1476,23 @@ int dso__load(struct dso *dso, struct map *map)
 	struct symsrc ss_[2];
 	struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
 	bool kmod;
+	bool perfmap;
 	unsigned char build_id[BUILD_ID_SIZE];
 	struct nscookie nsc;
+	char newmapname[PATH_MAX];
+	const char *map_path = dso->long_name;
+
+	perfmap = strncmp(dso->name, "/tmp/perf-", 10) == 0;
+	if (perfmap) {
+		if (dso->nsinfo && (dso__find_perf_map(newmapname,
+		    sizeof(newmapname), &dso->nsinfo) == 0)) {
+			map_path = newmapname;
+		}
+	}
 
 	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;
@@ -1471,19 +1515,19 @@ int dso__load(struct dso *dso, struct map *map)
 
 	dso->adjust_symbols = 0;
 
-	if (strncmp(dso->name, "/tmp/perf-", 10) == 0) {
+	if (perfmap) {
 		struct stat st;
 
-		if (lstat(dso->name, &st) < 0)
+		if (lstat(map_path, &st) < 0)
 			goto out;
 
 		if (!symbol_conf.force && st.st_uid && (st.st_uid != geteuid())) {
 			pr_warning("File %s not owned by current user or root, "
-				   "ignoring it (use -f to override).\n", dso->name);
+				   "ignoring it (use -f to override).\n", map_path);
 			goto out;
 		}
 
-		ret = dso__load_perf_map(dso, map);
+		ret = dso__load_perf_map(map_path, dso, map);
 		dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT :
 					     DSO_BINARY_TYPE__NOT_FOUND;
 		goto out;
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 tip/perf/core 3/6] perf probe: allow placing uprobes in alternate namespaces.
  2017-07-06  1:48       ` [PATCH v2 tip/perf/core 0/6] namespace tracing improvements Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 2/6] perf maps: lookup maps in both intitial mountns and inner mountns Krister Johansen
@ 2017-07-06  1:48         ` Krister Johansen
  2017-07-17 10:32           ` [PATCH] perf probe: Fix build failure for get_target_map() Ravi Bangoria
  2017-07-20  8:48           ` [tip:perf/core] perf probe: Allow placing uprobes in alternate namespaces tip-bot for Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 4/6] perf buildid-cache: support binary objects from other namespaces Krister Johansen
                           ` (2 subsequent siblings)
  5 siblings, 2 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-06  1:48 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Brendan Gregg
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel,
	Thomas-Mich Richter, Krister Johansen

Teaches perf how to place a uprobe on a file that's in a different mount
namespace.  The user must add the probe using the --target-ns argument
to perf probe.  Once it has been placed, it may be recorded against
without further namespace-specific commands.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/Documentation/perf-probe.txt |  9 ++++
 tools/perf/builtin-probe.c              | 43 ++++++++++++++++--
 tools/perf/util/namespaces.c            | 13 ++++++
 tools/perf/util/namespaces.h            |  2 +
 tools/perf/util/probe-event.c           | 79 ++++++++++++++++++++++-----------
 tools/perf/util/probe-event.h           | 10 +++--
 6 files changed, 123 insertions(+), 33 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 165c2b1..a42aabc 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -130,6 +130,11 @@ OPTIONS
 --max-probes=NUM::
 	Set the maximum number of probe points for an event. Default is 128.
 
+--target-ns=PID:
+	Obtain mount namespace information from the target pid.  This is
+	used when creating a uprobe for a process that resides in a
+	different mount namespace from the perf(1) utility.
+
 -x::
 --exec=PATH::
 	Specify path to the executable or shared library file for user
@@ -264,6 +269,10 @@ Add probes at malloc() function on libc
 
  ./perf probe -x /lib/libc.so.6 malloc or ./perf probe /lib/libc.so.6 malloc
 
+Add a uprobe to a target process running in a different mount namespace
+
+ ./perf probe --target-ns <target pid> -x /lib64/libc.so.6 malloc
+
 SEE ALSO
 --------
 linkperf:perf-trace[1], linkperf:perf-record[1], linkperf:perf-buildid-cache[1]
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index cf9f9e9..3fb98d5 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -58,6 +58,7 @@ static struct {
 	struct line_range line_range;
 	char *target;
 	struct strfilter *filter;
+	struct nsinfo *nsi;
 } params;
 
 /* Parse an event definition. Note that any error must die. */
@@ -80,6 +81,9 @@ static int parse_probe_event(const char *str)
 		params.target_used = true;
 	}
 
+	if (params.nsi)
+		pev->nsi = nsinfo__get(params.nsi);
+
 	/* Parse a perf-probe command into event */
 	ret = parse_perf_probe_command(str, pev);
 	pr_debug("%d arguments\n", pev->nargs);
@@ -189,7 +193,7 @@ static int opt_set_target(const struct option *opt, const char *str,
 
 		/* Expand given path to absolute path, except for modulename */
 		if (params.uprobes || strchr(str, '/')) {
-			tmp = realpath(str, NULL);
+			tmp = nsinfo__realpath(str, params.nsi);
 			if (!tmp) {
 				pr_warning("Failed to get the absolute path of %s: %m\n", str);
 				return ret;
@@ -208,6 +212,34 @@ static int opt_set_target(const struct option *opt, const char *str,
 	return ret;
 }
 
+static int opt_set_target_ns(const struct option *opt __maybe_unused,
+			     const char *str, int unset __maybe_unused)
+{
+	int ret = -ENOENT;
+	pid_t ns_pid;
+	struct nsinfo *nsip;
+
+	if (str) {
+		errno = 0;
+		ns_pid = (pid_t)strtol(str, NULL, 10);
+		if (errno != 0) {
+			ret = -errno;
+			pr_warning("Failed to parse %s as a pid: %s\n", str,
+				   strerror(errno));
+			return ret;
+		}
+		nsip = nsinfo__new(ns_pid);
+		if (nsip && nsip->need_setns)
+			params.nsi = nsinfo__get(nsip);
+		nsinfo__put(nsip);
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+
 /* Command option callbacks */
 
 #ifdef HAVE_DWARF_SUPPORT
@@ -299,6 +331,7 @@ static void cleanup_params(void)
 	line_range__clear(&params.line_range);
 	free(params.target);
 	strfilter__delete(params.filter);
+	nsinfo__put(params.nsi);
 	memset(&params, 0, sizeof(params));
 }
 
@@ -554,6 +587,8 @@ __cmd_probe(int argc, const char **argv)
 	OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
 	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
 		   "Look for files with symbols relative to this directory"),
+	OPT_CALLBACK(0, "target-ns", NULL, "pid",
+		     "target pid for namespace contexts", opt_set_target_ns),
 	OPT_END()
 	};
 	int ret;
@@ -634,15 +669,15 @@ __cmd_probe(int argc, const char **argv)
 			pr_err_with_code("  Error: Failed to show event list.", ret);
 		return ret;
 	case 'F':
-		ret = show_available_funcs(params.target, params.filter,
-					params.uprobes);
+		ret = show_available_funcs(params.target, params.nsi,
+					   params.filter, params.uprobes);
 		if (ret < 0)
 			pr_err_with_code("  Error: Failed to show functions.", ret);
 		return ret;
 #ifdef HAVE_DWARF_SUPPORT
 	case 'L':
 		ret = show_line_range(&params.line_range, params.target,
-				      params.uprobes);
+				      params.nsi, params.uprobes);
 		if (ret < 0)
 			pr_err_with_code("  Error: Failed to show lines.", ret);
 		return ret;
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index fc5f398..a58e911 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -11,6 +11,7 @@
 #include "event.h"
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <limits.h>
 #include <sched.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -233,3 +234,15 @@ void nsinfo__mountns_exit(struct nscookie *nc)
 		nc->newns = -1;
 	}
 }
+
+char *nsinfo__realpath(const char *path, struct nsinfo *nsi)
+{
+	char *rpath;
+	struct nscookie nsc;
+
+	nsinfo__mountns_enter(nsi, &nsc);
+	rpath = realpath(path, NULL);
+	nsinfo__mountns_exit(&nsc);
+
+	return rpath;
+}
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index f19aa41..05d8260 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -49,6 +49,8 @@ void nsinfo__put(struct nsinfo *nsi);
 void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc);
 void nsinfo__mountns_exit(struct nscookie *nc);
 
+char *nsinfo__realpath(const char *path, struct nsinfo *nsi);
+
 static inline void __nsinfo__zput(struct nsinfo **nsip)
 {
 	if (nsip) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index 84e7e69..dce4f12 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -184,13 +184,19 @@ static struct map *kernel_get_module_map(const char *module)
 	return NULL;
 }
 
-struct map *get_target_map(const char *target, bool user)
+struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user)
 {
 	/* Init maps of given executable or kernel */
-	if (user)
-		return dso__new_map(target);
-	else
+	if (user) {
+		struct map *map;
+
+		map = dso__new_map(target);
+		if (map && map->dso)
+			map->dso->nsinfo = nsinfo__get(nsi);
+		return map;
+	} else {
 		return kernel_get_module_map(target);
+	}
 }
 
 static int convert_exec_to_group(const char *exec, char **result)
@@ -366,7 +372,8 @@ static int kernel_get_module_dso(const char *module, struct dso **pdso)
 static int find_alternative_probe_point(struct debuginfo *dinfo,
 					struct perf_probe_point *pp,
 					struct perf_probe_point *result,
-					const char *target, bool uprobes)
+					const char *target, struct nsinfo *nsi,
+					bool uprobes)
 {
 	struct map *map = NULL;
 	struct symbol *sym;
@@ -377,7 +384,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
 	if (!pp->function || pp->file)
 		return -ENOTSUP;
 
-	map = get_target_map(target, uprobes);
+	map = get_target_map(target, nsi, uprobes);
 	if (!map)
 		return -EINVAL;
 
@@ -421,8 +428,8 @@ static int get_alternative_probe_event(struct debuginfo *dinfo,
 
 	memcpy(tmp, &pev->point, sizeof(*tmp));
 	memset(&pev->point, 0, sizeof(pev->point));
-	ret = find_alternative_probe_point(dinfo, tmp, &pev->point,
-					   pev->target, pev->uprobes);
+	ret = find_alternative_probe_point(dinfo, tmp, &pev->point, pev->target,
+					   pev->nsi, pev->uprobes);
 	if (ret < 0)
 		memcpy(&pev->point, tmp, sizeof(*tmp));
 
@@ -444,7 +451,7 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
 	if (lr->end != INT_MAX)
 		len = lr->end - lr->start;
 	ret = find_alternative_probe_point(dinfo, &pp, &result,
-					   target, user);
+					   target, NULL, user);
 	if (!ret) {
 		lr->function = result.function;
 		lr->file = result.file;
@@ -457,12 +464,14 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
 }
 
 /* Open new debuginfo of given module */
-static struct debuginfo *open_debuginfo(const char *module, bool silent)
+static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
+					bool silent)
 {
 	const char *path = module;
 	char reason[STRERR_BUFSIZE];
 	struct debuginfo *ret = NULL;
 	struct dso *dso = NULL;
+	struct nscookie nsc;
 	int err;
 
 	if (!module || !strchr(module, '/')) {
@@ -480,6 +489,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
 		}
 		path = dso->long_name;
 	}
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = debuginfo__new(path);
 	if (!ret && !silent) {
 		pr_warning("The %s file has no debug information.\n", path);
@@ -489,6 +499,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
 			pr_warning("Rebuild with -g, ");
 		pr_warning("or install an appropriate debuginfo package.\n");
 	}
+	nsinfo__mountns_exit(&nsc);
 	return ret;
 }
 
@@ -516,7 +527,7 @@ static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
 		goto out;
 	}
 
-	debuginfo_cache = open_debuginfo(module, silent);
+	debuginfo_cache = open_debuginfo(module, NULL, silent);
 	if (!debuginfo_cache)
 		zfree(&debuginfo_cache_path);
 out:
@@ -531,14 +542,18 @@ static void debuginfo_cache__exit(void)
 }
 
 
-static int get_text_start_address(const char *exec, unsigned long *address)
+static int get_text_start_address(const char *exec, unsigned long *address,
+				  struct nsinfo *nsi)
 {
 	Elf *elf;
 	GElf_Ehdr ehdr;
 	GElf_Shdr shdr;
 	int fd, ret = -ENOENT;
+	struct nscookie nsc;
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	fd = open(exec, O_RDONLY);
+	nsinfo__mountns_exit(&nsc);
 	if (fd < 0)
 		return -errno;
 
@@ -582,7 +597,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
 			ret = -EINVAL;
 			goto error;
 		}
-		ret = get_text_start_address(tp->module, &stext);
+		ret = get_text_start_address(tp->module, &stext, NULL);
 		if (ret < 0)
 			goto error;
 		addr += stext;
@@ -659,7 +674,7 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
 
 	/* Prepare a map for offline binary */
 	map = dso__new_map(pathname);
-	if (!map || get_text_start_address(pathname, &stext) < 0) {
+	if (!map || get_text_start_address(pathname, &stext, NULL) < 0) {
 		pr_warning("Failed to get ELF symbols for %s\n", pathname);
 		return -EINVAL;
 	}
@@ -676,7 +691,8 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
 }
 
 static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
-					  int ntevs, const char *exec)
+					  int ntevs, const char *exec,
+					  struct nsinfo *nsi)
 {
 	int i, ret = 0;
 	unsigned long stext = 0;
@@ -684,7 +700,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
 	if (!exec)
 		return 0;
 
-	ret = get_text_start_address(exec, &stext);
+	ret = get_text_start_address(exec, &stext, nsi);
 	if (ret < 0)
 		return ret;
 
@@ -715,7 +731,7 @@ post_process_module_probe_trace_events(struct probe_trace_event *tevs,
 	if (!module)
 		return 0;
 
-	map = get_target_map(module, false);
+	map = get_target_map(module, NULL, false);
 	if (!map || debuginfo__get_text_offset(dinfo, &text_offs, true) < 0) {
 		pr_warning("Failed to get ELF symbols for %s\n", module);
 		return -EINVAL;
@@ -802,7 +818,8 @@ static int post_process_probe_trace_events(struct perf_probe_event *pev,
 	int ret;
 
 	if (uprobe)
-		ret = add_exec_to_probe_trace_events(tevs, ntevs, module);
+		ret = add_exec_to_probe_trace_events(tevs, ntevs, module,
+						     pev->nsi);
 	else if (module)
 		/* Currently ref_reloc_sym based probe is not for drivers */
 		ret = post_process_module_probe_trace_events(tevs, ntevs,
@@ -825,7 +842,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 	struct debuginfo *dinfo;
 	int ntevs, ret = 0;
 
-	dinfo = open_debuginfo(pev->target, !need_dwarf);
+	dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf);
 	if (!dinfo) {
 		if (need_dwarf)
 			return -ENOENT;
@@ -945,7 +962,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
 	char sbuf[STRERR_BUFSIZE];
 
 	/* Search a line range */
-	dinfo = open_debuginfo(module, false);
+	dinfo = open_debuginfo(module, NULL, false);
 	if (!dinfo)
 		return -ENOENT;
 
@@ -1021,14 +1038,18 @@ static int __show_line_range(struct line_range *lr, const char *module,
 	return ret;
 }
 
-int show_line_range(struct line_range *lr, const char *module, bool user)
+int show_line_range(struct line_range *lr, const char *module,
+		    struct nsinfo *nsi, bool user)
 {
 	int ret;
+	struct nscookie nsc;
 
 	ret = init_probe_symbol_maps(user);
 	if (ret < 0)
 		return ret;
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = __show_line_range(lr, module, user);
+	nsinfo__mountns_exit(&nsc);
 	exit_probe_symbol_maps();
 
 	return ret;
@@ -1111,7 +1132,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
 	if (ret < 0)
 		return ret;
 
-	dinfo = open_debuginfo(pevs->target, false);
+	dinfo = open_debuginfo(pevs->target, pevs->nsi, false);
 	if (!dinfo) {
 		ret = -ENOENT;
 		goto out;
@@ -2703,6 +2724,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	struct probe_trace_event *tev = NULL;
 	struct probe_cache *cache = NULL;
 	struct strlist *namelist[2] = {NULL, NULL};
+	struct nscookie nsc;
 
 	up = pev->uprobes ? 1 : 0;
 	fd[up] = __open_probe_file_and_namelist(up, &namelist[up]);
@@ -2729,7 +2751,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 		if (ret < 0)
 			break;
 
+		nsinfo__mountns_enter(pev->nsi, &nsc);
 		ret = probe_file__add_event(fd[up], tev);
+		nsinfo__mountns_exit(&nsc);
 		if (ret < 0)
 			break;
 
@@ -2805,7 +2829,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
 	int ret, i, j, skipped = 0;
 	char *mod_name;
 
-	map = get_target_map(pev->target, pev->uprobes);
+	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
 	if (!map) {
 		ret = -EINVAL;
 		goto out;
@@ -3345,13 +3369,16 @@ int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 {
 	int i, j;
+	struct perf_probe_event *pev;
 
 	/* Loop 3: cleanup and free trace events  */
 	for (i = 0; i < npevs; i++) {
+		pev = &pevs[i];
 		for (j = 0; j < pevs[i].ntevs; j++)
 			clear_probe_trace_event(&pevs[i].tevs[j]);
 		zfree(&pevs[i].tevs);
 		pevs[i].ntevs = 0;
+		nsinfo__zput(pev->nsi);
 		clear_perf_probe_event(&pevs[i]);
 	}
 }
@@ -3409,8 +3436,8 @@ int del_perf_probe_events(struct strfilter *filter)
 	return ret;
 }
 
-int show_available_funcs(const char *target, struct strfilter *_filter,
-					bool user)
+int show_available_funcs(const char *target, struct nsinfo *nsi,
+			 struct strfilter *_filter, bool user)
 {
         struct rb_node *nd;
 	struct map *map;
@@ -3421,7 +3448,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
 		return ret;
 
 	/* Get a symbol map */
-	map = get_target_map(target, user);
+	map = get_target_map(target, nsi, user);
 	if (!map) {
 		pr_err("Failed to get a map for %s\n", (target) ? : "kernel");
 		return -EINVAL;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5812947..078681d 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -4,6 +4,7 @@
 #include <linux/compiler.h>
 #include <stdbool.h>
 #include "intlist.h"
+#include "namespaces.h"
 
 /* Probe related configurations */
 struct probe_conf {
@@ -92,6 +93,7 @@ struct perf_probe_event {
 	struct perf_probe_arg	*args;	/* Arguments */
 	struct probe_trace_event *tevs;
 	int			ntevs;
+	struct nsinfo		*nsi;	/* Target namespace */
 };
 
 /* Line range */
@@ -163,10 +165,12 @@ int show_perf_probe_event(const char *group, const char *event,
 			  struct perf_probe_event *pev,
 			  const char *module, bool use_stdout);
 int show_perf_probe_events(struct strfilter *filter);
-int show_line_range(struct line_range *lr, const char *module, bool user);
+int show_line_range(struct line_range *lr, const char *module,
+		    struct nsinfo *nsi, bool user);
 int show_available_vars(struct perf_probe_event *pevs, int npevs,
 			struct strfilter *filter);
-int show_available_funcs(const char *module, struct strfilter *filter, bool user);
+int show_available_funcs(const char *module, struct nsinfo *nsi,
+			 struct strfilter *filter, bool user);
 void arch__fix_tev_from_maps(struct perf_probe_event *pev,
 			     struct probe_trace_event *tev, struct map *map,
 			     struct symbol *sym);
@@ -180,7 +184,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...) __printf(3, 4);
 int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
 			    struct perf_probe_arg *pvar);
 
-struct map *get_target_map(const char *target, bool user);
+struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user);
 
 void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
 					   int ntevs);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 tip/perf/core 4/6] perf buildid-cache: support binary objects from other namespaces
  2017-07-06  1:48       ` [PATCH v2 tip/perf/core 0/6] namespace tracing improvements Krister Johansen
                           ` (2 preceding siblings ...)
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 3/6] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
@ 2017-07-06  1:48         ` Krister Johansen
  2017-07-20  8:49           ` [tip:perf/core] perf buildid-cache: Support " tip-bot for Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 5/6] perf top: support lookup of symbols in other mount namespaces Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 6/6] perf buildid-cache: cache debuginfo Krister Johansen
  5 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-06  1:48 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Brendan Gregg
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel,
	Thomas-Mich Richter, Krister Johansen

Teach buildid-cache how to add, remove, and update binary objects from
other mount namespaces.  Allow probe events tracing binaries in
different namespaces to add their objects to the probe and build-id
caches too.  As a handy side effect, this also lets us access SDT probes
in binaries from alternate mount namespaces.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/Documentation/perf-buildid-cache.txt |  5 +++
 tools/perf/Documentation/perf-probe.txt         |  5 +++
 tools/perf/builtin-buildid-cache.c              | 52 ++++++++++++++++++-------
 tools/perf/builtin-probe.c                      |  2 +-
 tools/perf/tests/sdt.c                          |  4 +-
 tools/perf/util/build-id.c                      | 47 ++++++++++++++--------
 tools/perf/util/build-id.h                      |  9 +++--
 tools/perf/util/dso.c                           | 12 +++++-
 tools/perf/util/parse-events.c                  |  2 +-
 tools/perf/util/probe-event.c                   |  6 +--
 tools/perf/util/probe-file.c                    | 19 +++++----
 tools/perf/util/probe-file.h                    |  4 +-
 tools/perf/util/symbol.c                        | 19 ++++++---
 tools/perf/util/util.c                          | 34 +++++++++++++---
 tools/perf/util/util.h                          |  2 +
 15 files changed, 159 insertions(+), 63 deletions(-)

diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt
index 058064d..8468100 100644
--- a/tools/perf/Documentation/perf-buildid-cache.txt
+++ b/tools/perf/Documentation/perf-buildid-cache.txt
@@ -61,6 +61,11 @@ OPTIONS
 --verbose::
 	Be more verbose.
 
+--target-ns=PID:
+	Obtain mount namespace information from the target pid.  This is
+	used when creating a uprobe for a process that resides in a
+	different mount namespace from the perf(1) utility.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index a42aabc..d7e4869 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -273,6 +273,11 @@ Add a uprobe to a target process running in a different mount namespace
 
  ./perf probe --target-ns <target pid> -x /lib64/libc.so.6 malloc
 
+Add a USDT probe to a target process running in a different mount namespace
+
+ ./perf probe --target-ns <target pid> -x /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.121-0.b13.el7_3.x86_64/jre/lib/amd64/server/libjvm.so %sdt_hotspot:thread__sleep__end
+
+
 SEE ALSO
 --------
 linkperf:perf-trace[1], linkperf:perf-record[1], linkperf:perf-buildid-cache[1]
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 9eba7f1..d65bd86 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -14,6 +14,7 @@
 #include <unistd.h>
 #include "builtin.h"
 #include "perf.h"
+#include "namespaces.h"
 #include "util/cache.h"
 #include "util/debug.h"
 #include "util/header.h"
@@ -165,33 +166,41 @@ static int build_id_cache__add_kcore(const char *filename, bool force)
 	return 0;
 }
 
-static int build_id_cache__add_file(const char *filename)
+static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi)
 {
 	char sbuild_id[SBUILD_ID_SIZE];
 	u8 build_id[BUILD_ID_SIZE];
 	int err;
+	struct nscookie nsc;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
-	err = build_id_cache__add_s(sbuild_id, filename,
+	err = build_id_cache__add_s(sbuild_id, filename, nsi,
 				    false, false);
 	pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
 		 err ? "FAIL" : "Ok");
 	return err;
 }
 
-static int build_id_cache__remove_file(const char *filename)
+static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi)
 {
 	u8 build_id[BUILD_ID_SIZE];
 	char sbuild_id[SBUILD_ID_SIZE];
+	struct nscookie nsc;
 
 	int err;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
@@ -204,13 +213,13 @@ static int build_id_cache__remove_file(const char *filename)
 	return err;
 }
 
-static int build_id_cache__purge_path(const char *pathname)
+static int build_id_cache__purge_path(const char *pathname, struct nsinfo *nsi)
 {
 	struct strlist *list;
 	struct str_node *pos;
 	int err;
 
-	err = build_id_cache__list_build_ids(pathname, &list);
+	err = build_id_cache__list_build_ids(pathname, nsi, &list);
 	if (err)
 		goto out;
 
@@ -256,24 +265,30 @@ static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *f
 	return 0;
 }
 
-static int build_id_cache__update_file(const char *filename)
+static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi)
 {
 	u8 build_id[BUILD_ID_SIZE];
 	char sbuild_id[SBUILD_ID_SIZE];
+	struct nscookie nsc;
 
-	int err = 0;
+	int err;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
+	err = 0;
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
 	if (build_id_cache__cached(sbuild_id))
 		err = build_id_cache__remove_s(sbuild_id);
 
 	if (!err)
-		err = build_id_cache__add_s(sbuild_id, filename, false, false);
+		err = build_id_cache__add_s(sbuild_id, filename, nsi, false,
+					    false);
 
 	pr_debug("Updating %s %s: %s\n", sbuild_id, filename,
 		 err ? "FAIL" : "Ok");
@@ -286,6 +301,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 	struct strlist *list;
 	struct str_node *pos;
 	int ret = 0;
+	int ns_id = -1;
 	bool force = false;
 	char const *add_name_list_str = NULL,
 		   *remove_name_list_str = NULL,
@@ -299,6 +315,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		.mode  = PERF_DATA_MODE_READ,
 	};
 	struct perf_session *session = NULL;
+	struct nsinfo *nsi = NULL;
 
 	const struct option buildid_cache_options[] = {
 	OPT_STRING('a', "add", &add_name_list_str,
@@ -315,6 +332,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 	OPT_STRING('u', "update", &update_name_list_str, "file list",
 		    "file(s) to update"),
 	OPT_INCR('v', "verbose", &verbose, "be more verbose"),
+	OPT_INTEGER(0, "target-ns", &ns_id, "target pid for namespace context"),
 	OPT_END()
 	};
 	const char * const buildid_cache_usage[] = {
@@ -330,6 +348,9 @@ int cmd_buildid_cache(int argc, const char **argv)
 		     !missing_filename && !update_name_list_str))
 		usage_with_options(buildid_cache_usage, buildid_cache_options);
 
+	if (ns_id > 0)
+		nsi = nsinfo__new(ns_id);
+
 	if (missing_filename) {
 		file.path = missing_filename;
 		file.force = force;
@@ -348,7 +369,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(add_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__add_file(pos->s)) {
+				if (build_id_cache__add_file(pos->s, nsi)) {
 					if (errno == EEXIST) {
 						pr_debug("%s already in the cache\n",
 							 pos->s);
@@ -366,7 +387,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(remove_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__remove_file(pos->s)) {
+				if (build_id_cache__remove_file(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -384,7 +405,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(purge_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__purge_path(pos->s)) {
+				if (build_id_cache__purge_path(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -405,7 +426,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(update_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__update_file(pos->s)) {
+				if (build_id_cache__update_file(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -424,6 +445,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 
 out:
 	perf_session__delete(session);
+	nsinfo__zput(nsi);
 
 	return ret;
 }
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 3fb98d5..c006592 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -416,7 +416,7 @@ static int del_perf_probe_caches(struct strfilter *filter)
 	}
 
 	strlist__for_each_entry(nd, bidlist) {
-		cache = probe_cache__new(nd->s);
+		cache = probe_cache__new(nd->s, NULL);
 		if (!cache)
 			continue;
 		if (probe_cache__filter_purge(cache, filter) < 0 ||
diff --git a/tools/perf/tests/sdt.c b/tools/perf/tests/sdt.c
index 06eda67..5abe8ce 100644
--- a/tools/perf/tests/sdt.c
+++ b/tools/perf/tests/sdt.c
@@ -33,7 +33,7 @@ static int build_id_cache__add_file(const char *filename)
 	}
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
-	err = build_id_cache__add_s(sbuild_id, filename, false, false);
+	err = build_id_cache__add_s(sbuild_id, filename, NULL, false, false);
 	if (err < 0)
 		pr_debug("Failed to add build id cache of %s\n", filename);
 	return err;
@@ -54,7 +54,7 @@ static char *get_self_path(void)
 static int search_cached_probe(const char *target,
 			       const char *group, const char *event)
 {
-	struct probe_cache *cache = probe_cache__new(target);
+	struct probe_cache *cache = probe_cache__new(target, NULL);
 	int ret = 0;
 
 	if (!cache) {
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index e0148b0..f7bfd90 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -534,13 +534,14 @@ char *build_id_cache__complement(const char *incomplete_sbuild_id)
 }
 
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
-			       bool is_kallsyms, bool is_vdso)
+			       struct nsinfo *nsi, bool is_kallsyms,
+			       bool is_vdso)
 {
 	char *realname = (char *)name, *filename;
 	bool slash = is_kallsyms || is_vdso;
 
 	if (!slash) {
-		realname = realpath(name, NULL);
+		realname = nsinfo__realpath(name, nsi);
 		if (!realname)
 			return NULL;
 	}
@@ -556,13 +557,13 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
 	return filename;
 }
 
-int build_id_cache__list_build_ids(const char *pathname,
+int build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi,
 				   struct strlist **result)
 {
 	char *dir_name;
 	int ret = 0;
 
-	dir_name = build_id_cache__cachedir(NULL, pathname, false, false);
+	dir_name = build_id_cache__cachedir(NULL, pathname, nsi, false, false);
 	if (!dir_name)
 		return -ENOMEM;
 
@@ -576,16 +577,20 @@ int build_id_cache__list_build_ids(const char *pathname,
 
 #if defined(HAVE_LIBELF_SUPPORT) && defined(HAVE_GELF_GETNOTE_SUPPORT)
 static int build_id_cache__add_sdt_cache(const char *sbuild_id,
-					  const char *realname)
+					  const char *realname,
+					  struct nsinfo *nsi)
 {
 	struct probe_cache *cache;
 	int ret;
+	struct nscookie nsc;
 
-	cache = probe_cache__new(sbuild_id);
+	cache = probe_cache__new(sbuild_id, nsi);
 	if (!cache)
 		return -1;
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = probe_cache__scan_sdt(cache, realname);
+	nsinfo__mountns_exit(&nsc);
 	if (ret >= 0) {
 		pr_debug4("Found %d SDTs in %s\n", ret, realname);
 		if (probe_cache__commit(cache) < 0)
@@ -595,11 +600,11 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id,
 	return ret;
 }
 #else
-#define build_id_cache__add_sdt_cache(sbuild_id, realname) (0)
+#define build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) (0)
 #endif
 
 int build_id_cache__add_s(const char *sbuild_id, const char *name,
-			  bool is_kallsyms, bool is_vdso)
+			  struct nsinfo *nsi, bool is_kallsyms, bool is_vdso)
 {
 	const size_t size = PATH_MAX;
 	char *realname = NULL, *filename = NULL, *dir_name = NULL,
@@ -607,13 +612,16 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 	int err = -1;
 
 	if (!is_kallsyms) {
-		realname = realpath(name, NULL);
+		if (!is_vdso)
+			realname = nsinfo__realpath(name, nsi);
+		else
+			realname = realpath(name, NULL);
 		if (!realname)
 			goto out_free;
 	}
 
-	dir_name = build_id_cache__cachedir(sbuild_id, name,
-					    is_kallsyms, is_vdso);
+	dir_name = build_id_cache__cachedir(sbuild_id, name, nsi, is_kallsyms,
+					    is_vdso);
 	if (!dir_name)
 		goto out_free;
 
@@ -634,7 +642,10 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 
 	if (access(filename, F_OK)) {
 		if (is_kallsyms) {
-			 if (copyfile("/proc/kallsyms", filename))
+			if (copyfile("/proc/kallsyms", filename))
+				goto out_free;
+		} else if (nsi && nsi->need_setns) {
+			if (copyfile_ns(name, filename, nsi))
 				goto out_free;
 		} else if (link(realname, filename) && errno != EEXIST &&
 				copyfile(name, filename))
@@ -657,7 +668,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 		err = 0;
 
 	/* Update SDT cache : error is just warned */
-	if (realname && build_id_cache__add_sdt_cache(sbuild_id, realname) < 0)
+	if (realname &&
+	    build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) < 0)
 		pr_debug4("Failed to update/scan SDT cache for %s\n", realname);
 
 out_free:
@@ -670,14 +682,15 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 }
 
 static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
-				 const char *name, bool is_kallsyms,
-				 bool is_vdso)
+				 const char *name, struct nsinfo *nsi,
+				 bool is_kallsyms, bool is_vdso)
 {
 	char sbuild_id[SBUILD_ID_SIZE];
 
 	build_id__sprintf(build_id, build_id_size, sbuild_id);
 
-	return build_id_cache__add_s(sbuild_id, name, is_kallsyms, is_vdso);
+	return build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms,
+				     is_vdso);
 }
 
 bool build_id_cache__cached(const char *sbuild_id)
@@ -743,7 +756,7 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine)
 		name = nm;
 	}
 	return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
-				     is_kallsyms, is_vdso);
+				     dso->nsinfo, is_kallsyms, is_vdso);
 }
 
 static int __dsos__cache_build_ids(struct list_head *head,
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 96690a5..2397084 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -5,6 +5,7 @@
 #define SBUILD_ID_SIZE	(BUILD_ID_SIZE * 2 + 1)
 
 #include "tool.h"
+#include "namespaces.h"
 #include <linux/types.h>
 
 extern struct perf_tool build_id__mark_dso_hit_ops;
@@ -31,17 +32,19 @@ int perf_session__cache_build_ids(struct perf_session *session);
 char *build_id_cache__origname(const char *sbuild_id);
 char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size);
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
-			       bool is_kallsyms, bool is_vdso);
+			       struct nsinfo *nsi, bool is_kallsyms,
+			       bool is_vdso);
 
 struct strlist;
 
 struct strlist *build_id_cache__list_all(bool validonly);
 char *build_id_cache__complement(const char *incomplete_sbuild_id);
-int build_id_cache__list_build_ids(const char *pathname,
+int build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
 int build_id_cache__add_s(const char *sbuild_id,
-			  const char *name, bool is_kallsyms, bool is_vdso);
+			  const char *name, struct nsinfo *nsi,
+			  bool is_kallsyms, bool is_vdso);
 int build_id_cache__remove_s(const char *sbuild_id);
 
 extern char buildid_dir[];
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index beda40e..dc9b495 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -504,7 +504,14 @@ static void check_data_close(void);
  */
 static int open_dso(struct dso *dso, struct machine *machine)
 {
-	int fd = __open_dso(dso, machine);
+	int fd;
+	struct nscookie nsc;
+
+	if (dso->binary_type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		nsinfo__mountns_enter(dso->nsinfo, &nsc);
+	fd = __open_dso(dso, machine);
+	if (dso->binary_type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		nsinfo__mountns_exit(&nsc);
 
 	if (fd >= 0) {
 		dso__list_add(dso);
@@ -1302,6 +1309,7 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
 {
 	bool have_build_id = false;
 	struct dso *pos;
+	struct nscookie nsc;
 
 	list_for_each_entry(pos, head, node) {
 		if (with_hits && !pos->hit && !dso__is_vdso(pos))
@@ -1310,11 +1318,13 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
 			have_build_id = true;
 			continue;
 		}
+		nsinfo__mountns_enter(pos->nsinfo, &nsc);
 		if (filename__read_build_id(pos->long_name, pos->build_id,
 					    sizeof(pos->build_id)) > 0) {
 			have_build_id	  = true;
 			pos->has_build_id = true;
 		}
+		nsinfo__mountns_exit(&nsc);
 	}
 
 	return have_build_id;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 01e779b..84e3010 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -2124,7 +2124,7 @@ void print_sdt_events(const char *subsys_glob, const char *event_glob,
 		return;
 	}
 	strlist__for_each_entry(nd, bidlist) {
-		pcache = probe_cache__new(nd->s);
+		pcache = probe_cache__new(nd->s, NULL);
 		if (!pcache)
 			continue;
 		list_for_each_entry(ent, &pcache->entries, node) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index dce4f12..db67029 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2768,7 +2768,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	if (ret == -EINVAL && pev->uprobes)
 		warn_uprobe_event_compat(tev);
 	if (ret == 0 && probe_conf.cache) {
-		cache = probe_cache__new(pev->target);
+		cache = probe_cache__new(pev->target, pev->nsi);
 		if (!cache ||
 		    probe_cache__add_entry(cache, pev, tevs, ntevs) < 0 ||
 		    probe_cache__commit(cache) < 0)
@@ -3118,7 +3118,7 @@ static int find_cached_events(struct perf_probe_event *pev,
 	int ntevs = 0;
 	int ret = 0;
 
-	cache = probe_cache__new(target);
+	cache = probe_cache__new(target, pev->nsi);
 	/* Return 0 ("not found") if the target has no probe cache. */
 	if (!cache)
 		return 0;
@@ -3208,7 +3208,7 @@ static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
 		else
 			return find_cached_events(pev, tevs, pev->target);
 	}
-	cache = probe_cache__new(pev->target);
+	cache = probe_cache__new(pev->target, pev->nsi);
 	if (!cache)
 		return 0;
 
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
index d679389..cdf8d83 100644
--- a/tools/perf/util/probe-file.c
+++ b/tools/perf/util/probe-file.c
@@ -412,13 +412,15 @@ int probe_cache_entry__get_event(struct probe_cache_entry *entry,
 }
 
 /* For the kernel probe caches, pass target = NULL or DSO__NAME_KALLSYMS */
-static int probe_cache__open(struct probe_cache *pcache, const char *target)
+static int probe_cache__open(struct probe_cache *pcache, const char *target,
+			     struct nsinfo *nsi)
 {
 	char cpath[PATH_MAX];
 	char sbuildid[SBUILD_ID_SIZE];
 	char *dir_name = NULL;
 	bool is_kallsyms = false;
 	int ret, fd;
+	struct nscookie nsc;
 
 	if (target && build_id_cache__cached(target)) {
 		/* This is a cached buildid */
@@ -431,8 +433,11 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 		target = DSO__NAME_KALLSYMS;
 		is_kallsyms = true;
 		ret = sysfs__sprintf_build_id("/", sbuildid);
-	} else
+	} else {
+		nsinfo__mountns_enter(nsi, &nsc);
 		ret = filename__sprintf_build_id(target, sbuildid);
+		nsinfo__mountns_exit(&nsc);
+	}
 
 	if (ret < 0) {
 		pr_debug("Failed to get build-id from %s.\n", target);
@@ -441,7 +446,7 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 
 	/* If we have no buildid cache, make it */
 	if (!build_id_cache__cached(sbuildid)) {
-		ret = build_id_cache__add_s(sbuildid, target,
+		ret = build_id_cache__add_s(sbuildid, target, nsi,
 					    is_kallsyms, NULL);
 		if (ret < 0) {
 			pr_debug("Failed to add build-id cache: %s\n", target);
@@ -449,7 +454,7 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 		}
 	}
 
-	dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms,
+	dir_name = build_id_cache__cachedir(sbuildid, target, nsi, is_kallsyms,
 					    false);
 found:
 	if (!dir_name) {
@@ -554,7 +559,7 @@ void probe_cache__delete(struct probe_cache *pcache)
 	free(pcache);
 }
 
-struct probe_cache *probe_cache__new(const char *target)
+struct probe_cache *probe_cache__new(const char *target, struct nsinfo *nsi)
 {
 	struct probe_cache *pcache = probe_cache__alloc();
 	int ret;
@@ -562,7 +567,7 @@ struct probe_cache *probe_cache__new(const char *target)
 	if (!pcache)
 		return NULL;
 
-	ret = probe_cache__open(pcache, target);
+	ret = probe_cache__open(pcache, target, nsi);
 	if (ret < 0) {
 		pr_debug("Cache open error: %d\n", ret);
 		goto out_err;
@@ -974,7 +979,7 @@ int probe_cache__show_all_caches(struct strfilter *filter)
 		return -EINVAL;
 	}
 	strlist__for_each_entry(nd, bidlist) {
-		pcache = probe_cache__new(nd->s);
+		pcache = probe_cache__new(nd->s, NULL);
 		if (!pcache)
 			continue;
 		if (!list_empty(&pcache->entries)) {
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h
index 5ecc9d3..2ca4163 100644
--- a/tools/perf/util/probe-file.h
+++ b/tools/perf/util/probe-file.h
@@ -51,7 +51,7 @@ int probe_file__del_strlist(int fd, struct strlist *namelist);
 int probe_cache_entry__get_event(struct probe_cache_entry *entry,
 				 struct probe_trace_event **tevs);
 
-struct probe_cache *probe_cache__new(const char *target);
+struct probe_cache *probe_cache__new(const char *target, struct nsinfo *nsi);
 int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct perf_probe_event *pev,
 			   struct probe_trace_event *tevs, int ntevs);
@@ -69,7 +69,7 @@ int probe_cache__show_all_caches(struct strfilter *filter);
 bool probe_type_is_available(enum probe_type type);
 bool kretprobe_offset_is_supported(void);
 #else	/* ! HAVE_LIBELF_SUPPORT */
-static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused)
+static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused)
 {
 	return NULL;
 }
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 21c97cc..8c7bae5 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1464,7 +1464,6 @@ static int dso__find_perf_map(char *filebuf, size_t bufsz,
 	return rc;
 }
 
-
 int dso__load(struct dso *dso, struct map *map)
 {
 	char *name;
@@ -1565,6 +1564,8 @@ int dso__load(struct dso *dso, struct map *map)
 	for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) {
 		struct symsrc *ss = &ss_[ss_pos];
 		bool next_slot = false;
+		bool is_reg;
+		int sirc;
 
 		enum dso_binary_type symtab_type = binary_type_symtab[i];
 
@@ -1575,12 +1576,20 @@ int dso__load(struct dso *dso, struct map *map)
 						   root_dir, name, PATH_MAX))
 			continue;
 
-		if (!is_regular_file(name))
-			continue;
+		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+			nsinfo__mountns_exit(&nsc);
+
+		is_reg = is_regular_file(name);
+		sirc = symsrc__init(ss, dso, name, symtab_type);
 
-		/* Name is now the name of the next image to try */
-		if (symsrc__init(ss, dso, name, symtab_type) < 0)
+		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+			nsinfo__mountns_enter(dso->nsinfo, &nsc);
+
+		if (!is_reg || sirc < 0) {
+			if (sirc >= 0)
+				symsrc__destroy(ss);
 			continue;
+		}
 
 		if (!syms_ss && symsrc__has_symtab(ss)) {
 			syms_ss = ss;
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 988111e..9e4ea85 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -143,13 +143,17 @@ struct strlist *lsdir(const char *name,
 	return list;
 }
 
-static int slow_copyfile(const char *from, const char *to)
+static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)
 {
 	int err = -1;
 	char *line = NULL;
 	size_t n;
-	FILE *from_fp = fopen(from, "r"), *to_fp;
+	FILE *from_fp, *to_fp;
+	struct nscookie nsc;
 
+	nsinfo__mountns_enter(nsi, &nsc);
+	from_fp = fopen(from, "r");
+	nsinfo__mountns_exit(&nsc);
 	if (from_fp == NULL)
 		goto out;
 
@@ -198,15 +202,21 @@ int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
 	return size ? -1 : 0;
 }
 
-int copyfile_mode(const char *from, const char *to, mode_t mode)
+static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
+			    struct nsinfo *nsi)
 {
 	int fromfd, tofd;
 	struct stat st;
-	int err = -1;
+	int err;
 	char *tmp = NULL, *ptr = NULL;
+	struct nscookie nsc;
 
-	if (stat(from, &st))
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = stat(from, &st);
+	nsinfo__mountns_exit(&nsc);
+	if (err)
 		goto out;
+	err = -1;
 
 	/* extra 'x' at the end is to reserve space for '.' */
 	if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
@@ -227,11 +237,13 @@ int copyfile_mode(const char *from, const char *to, mode_t mode)
 		goto out_close_to;
 
 	if (st.st_size == 0) { /* /proc? do it slowly... */
-		err = slow_copyfile(from, tmp);
+		err = slow_copyfile(from, tmp, nsi);
 		goto out_close_to;
 	}
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	fromfd = open(from, O_RDONLY);
+	nsinfo__mountns_exit(&nsc);
 	if (fromfd < 0)
 		goto out_close_to;
 
@@ -248,6 +260,16 @@ int copyfile_mode(const char *from, const char *to, mode_t mode)
 	return err;
 }
 
+int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi)
+{
+	return copyfile_mode_ns(from, to, 0755, nsi);
+}
+
+int copyfile_mode(const char *from, const char *to, mode_t mode)
+{
+	return copyfile_mode_ns(from, to, mode, NULL);
+}
+
 int copyfile(const char *from, const char *to)
 {
 	return copyfile_mode(from, to, 0755);
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 2c9e58a..6a08cc8 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -12,6 +12,7 @@
 #include <stdarg.h>
 #include <linux/compiler.h>
 #include <linux/types.h>
+#include "namespaces.h"
 
 /* General helper functions */
 void usage(const char *err) __noreturn;
@@ -33,6 +34,7 @@ struct strlist *lsdir(const char *name, bool (*filter)(const char *, struct dire
 bool lsdir_no_dot_filter(const char *name, struct dirent *d);
 int copyfile(const char *from, const char *to);
 int copyfile_mode(const char *from, const char *to, mode_t mode);
+int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi);
 int copyfile_offset(int fromfd, loff_t from_ofs, int tofd, loff_t to_ofs, u64 size);
 
 ssize_t readn(int fd, void *buf, size_t n);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 tip/perf/core 5/6] perf top: support lookup of symbols in other mount namespaces.
  2017-07-06  1:48       ` [PATCH v2 tip/perf/core 0/6] namespace tracing improvements Krister Johansen
                           ` (3 preceding siblings ...)
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 4/6] perf buildid-cache: support binary objects from other namespaces Krister Johansen
@ 2017-07-06  1:48         ` Krister Johansen
  2017-07-21 17:10           ` Arnaldo Carvalho de Melo
  2017-07-26 17:23           ` [tip:perf/core] perf top: Support " tip-bot for Krister Johansen
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 6/6] perf buildid-cache: cache debuginfo Krister Johansen
  5 siblings, 2 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-06  1:48 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Brendan Gregg
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel,
	Thomas-Mich Richter, Krister Johansen

The perf top command needs to unshare its fs from the helper threads
in order to successfully setns(2) during its symbol lookup.  It also
needs to impelement a force flag to ignore ownership of perf-<pid>.map
files.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/Documentation/perf-top.txt |  4 ++++
 tools/perf/builtin-top.c              | 15 +++++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index e71d638..d864ea6 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -237,6 +237,10 @@ Default is to monitor all CPUS.
 --hierarchy::
 	Enable hierarchy output.
 
+--force::
+	Don't do ownership validation.
+
+
 INTERACTIVE PROMPTING KEYS
 --------------------------
 
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index 6052376..ef2aef4 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -586,6 +586,13 @@ static void *display_thread_tui(void *arg)
 		.refresh	= top->delay_secs,
 	};
 
+	/* In order to read symbols from other namespaces perf to  needs to call
+	 * setns(2).  This isn't permitted if the struct_fs has multiple users.
+	 * unshare(2) the fs so that we may continue to setns into namespaces
+	 * that we're observing.
+	 */
+	(void) unshare(CLONE_FS);
+
 	perf_top__sort_new_samples(top);
 
 	/*
@@ -627,6 +634,13 @@ static void *display_thread(void *arg)
 	struct perf_top *top = arg;
 	int delay_msecs, c;
 
+	/* In order to read symbols from other namespaces perf to  needs to call
+	 * setns(2).  This isn't permitted if the struct_fs has multiple users.
+	 * unshare(2) the fs so that we may continue to setns into namespaces
+	 * that we're observing.
+	 */
+	(void) unshare(CLONE_FS);
+
 	display_setup_sig();
 	pthread__unblock_sigwinch();
 repeat:
@@ -1205,6 +1219,7 @@ int cmd_top(int argc, const char **argv)
 		    "Show raw trace event output (do not use print fmt or plugins)"),
 	OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy,
 		    "Show entries in a hierarchy"),
+	OPT_BOOLEAN(0, "force", &symbol_conf.force, "don't complain, do it"),
 	OPT_END()
 	};
 	const char * const top_usage[] = {
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [PATCH v2 tip/perf/core 6/6] perf buildid-cache: cache debuginfo
  2017-07-06  1:48       ` [PATCH v2 tip/perf/core 0/6] namespace tracing improvements Krister Johansen
                           ` (4 preceding siblings ...)
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 5/6] perf top: support lookup of symbols in other mount namespaces Krister Johansen
@ 2017-07-06  1:48         ` Krister Johansen
  2017-07-20  8:49           ` [tip:perf/core] perf buildid-cache: Cache debuginfo tip-bot for Krister Johansen
  5 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-06  1:48 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Brendan Gregg
  Cc: Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel,
	Thomas-Mich Richter, Krister Johansen

If a stripped binary is placed in the cache, the user is in a situation
where there's a cached elf file present, but it doesn't have any symtab
to use for name resolution.  Grab the debuginfo for binaries that don't
end in .ko.  This yields a better chance of resolving symbols from
older traces.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
---
 tools/perf/builtin-buildid-cache.c |  2 +-
 tools/perf/util/annotate.c         |  2 +-
 tools/perf/util/build-id.c         | 72 +++++++++++++++++++++++++++++++++++---
 tools/perf/util/build-id.h         |  3 +-
 tools/perf/util/dso.c              |  8 ++++-
 tools/perf/util/dso.h              |  1 +
 tools/perf/util/machine.c          |  3 +-
 tools/perf/util/symbol.c           | 12 +++++--
 8 files changed, 90 insertions(+), 13 deletions(-)

diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index d65bd86..e3eb624 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -243,7 +243,7 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
 	char filename[PATH_MAX];
 	u8 build_id[BUILD_ID_SIZE];
 
-	if (dso__build_id_filename(dso, filename, sizeof(filename)) &&
+	if (dso__build_id_filename(dso, filename, sizeof(filename), false) &&
 	    filename__read_build_id(filename, build_id,
 				    sizeof(build_id)) != sizeof(build_id)) {
 		if (errno == ENOENT)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index be1caab..7ce940d 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1327,7 +1327,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
 	    !dso__is_kcore(dso))
 		return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX;
 
-	build_id_filename = dso__build_id_filename(dso, NULL, 0);
+	build_id_filename = dso__build_id_filename(dso, NULL, 0, false);
 	if (build_id_filename) {
 		__symbol__join_symfs(filename, filename_size, build_id_filename);
 		free(build_id_filename);
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index f7bfd90..e966515 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -243,12 +243,15 @@ static bool build_id_cache__valid_id(char *sbuild_id)
 	return result;
 }
 
-static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso)
+static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso,
+					    bool is_debug)
 {
-	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf");
+	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : (is_debug ?
+	    "debug" : "elf"));
 }
 
-char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
+char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size,
+			     bool is_debug)
 {
 	bool is_kallsyms = dso__is_kallsyms((struct dso *)dso);
 	bool is_vdso = dso__is_vdso((struct dso *)dso);
@@ -270,7 +273,8 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
 		ret = asnprintf(&bf, size, "%s", linkname);
 	else
 		ret = asnprintf(&bf, size, "%s/%s", linkname,
-			 build_id_cache__basename(is_kallsyms, is_vdso));
+			 build_id_cache__basename(is_kallsyms, is_vdso,
+						  is_debug));
 	if (ret < 0 || (!alloc && size < (unsigned int)ret))
 		bf = NULL;
 	free(linkname);
@@ -603,12 +607,40 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id,
 #define build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) (0)
 #endif
 
+static char *build_id_cache__find_debug(const char *sbuild_id,
+					struct nsinfo *nsi)
+{
+	char *realname = NULL;
+	char *debugfile;
+	struct nscookie nsc;
+	size_t len = 0;
+
+	debugfile = calloc(1, PATH_MAX);
+	if (!debugfile)
+		goto out;
+
+	len = __symbol__join_symfs(debugfile, PATH_MAX,
+				   "/usr/lib/debug/.build-id/");
+	snprintf(debugfile + len, PATH_MAX - len, "%.2s/%s.debug", sbuild_id,
+		 sbuild_id + 2);
+
+	nsinfo__mountns_enter(nsi, &nsc);
+	realname = realpath(debugfile, NULL);
+	if (realname && access(realname, R_OK))
+		zfree(&realname);
+	nsinfo__mountns_exit(&nsc);
+out:
+	free(debugfile);
+	return realname;
+}
+
 int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			  struct nsinfo *nsi, bool is_kallsyms, bool is_vdso)
 {
 	const size_t size = PATH_MAX;
 	char *realname = NULL, *filename = NULL, *dir_name = NULL,
 	     *linkname = zalloc(size), *tmp;
+	char *debugfile = NULL;
 	int err = -1;
 
 	if (!is_kallsyms) {
@@ -635,7 +667,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 
 	/* Save the allocated buildid dirname */
 	if (asprintf(&filename, "%s/%s", dir_name,
-		     build_id_cache__basename(is_kallsyms, is_vdso)) < 0) {
+		     build_id_cache__basename(is_kallsyms, is_vdso,
+		     false)) < 0) {
 		filename = NULL;
 		goto out_free;
 	}
@@ -652,6 +685,34 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			goto out_free;
 	}
 
+	/* Some binaries are stripped, but have .debug files with their symbol
+	 * table.  Check to see if we can locate one of those, since the elf
+	 * file itself may not be very useful to users of our tools without a
+	 * symtab.
+	 */
+	if (!is_kallsyms && !is_vdso &&
+	    strncmp(".ko", name + strlen(name) - 3, 3)) {
+		debugfile = build_id_cache__find_debug(sbuild_id, nsi);
+		if (debugfile) {
+			zfree(&filename);
+			if (asprintf(&filename, "%s/%s", dir_name,
+			    build_id_cache__basename(false, false, true)) < 0) {
+				filename = NULL;
+				goto out_free;
+			}
+			if (access(filename, F_OK)) {
+				if (nsi && nsi->need_setns) {
+					if (copyfile_ns(debugfile, filename,
+							nsi))
+						goto out_free;
+				} else if (link(debugfile, filename) &&
+						errno != EEXIST &&
+						copyfile(debugfile, filename))
+					goto out_free;
+			}
+		}
+	}
+
 	if (!build_id_cache__linkname(sbuild_id, linkname, size))
 		goto out_free;
 	tmp = strrchr(linkname, '/');
@@ -676,6 +737,7 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 	if (!is_kallsyms)
 		free(realname);
 	free(filename);
+	free(debugfile);
 	free(dir_name);
 	free(linkname);
 	return err;
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 2397084..113dc06 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -17,7 +17,8 @@ int filename__sprintf_build_id(const char *pathname, char *sbuild_id);
 char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf,
 				    size_t size);
 
-char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size);
+char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size,
+			     bool is_debug);
 
 int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
 			   struct perf_sample *sample, struct perf_evsel *evsel,
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index dc9b495..b9e087f 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -32,6 +32,7 @@ char dso__symtab_origin(const struct dso *dso)
 		[DSO_BINARY_TYPE__JAVA_JIT]			= 'j',
 		[DSO_BINARY_TYPE__DEBUGLINK]			= 'l',
 		[DSO_BINARY_TYPE__BUILD_ID_CACHE]		= 'B',
+		[DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO]	= 'D',
 		[DSO_BINARY_TYPE__FEDORA_DEBUGINFO]		= 'f',
 		[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]		= 'u',
 		[DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO]	= 'o',
@@ -97,7 +98,12 @@ int dso__read_binary_type_filename(const struct dso *dso,
 		break;
 	}
 	case DSO_BINARY_TYPE__BUILD_ID_CACHE:
-		if (dso__build_id_filename(dso, filename, size) == NULL)
+		if (dso__build_id_filename(dso, filename, size, false) == NULL)
+			ret = -1;
+		break;
+
+	case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+		if (dso__build_id_filename(dso, filename, size, true) == NULL)
 			ret = -1;
 		break;
 
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 78ec637..f886141 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -21,6 +21,7 @@ enum dso_binary_type {
 	DSO_BINARY_TYPE__JAVA_JIT,
 	DSO_BINARY_TYPE__DEBUGLINK,
 	DSO_BINARY_TYPE__BUILD_ID_CACHE,
+	DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO,
 	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
 	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
 	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 4b0f16c..beae2a4 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -705,7 +705,8 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp)
 
 	if (kdso->has_build_id) {
 		char filename[PATH_MAX];
-		if (dso__build_id_filename(kdso, filename, sizeof(filename)))
+		if (dso__build_id_filename(kdso, filename, sizeof(filename),
+					   false))
 			printed += fprintf(fp, "[0] %s\n", filename);
 	}
 
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 8c7bae5..971b990 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -53,6 +53,7 @@ static enum dso_binary_type binary_type_symtab[] = {
 	DSO_BINARY_TYPE__JAVA_JIT,
 	DSO_BINARY_TYPE__DEBUGLINK,
 	DSO_BINARY_TYPE__BUILD_ID_CACHE,
+	DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO,
 	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
 	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
 	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
@@ -1418,6 +1419,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
 		return kmod && dso->symtab_type == type;
 
 	case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+	case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
 		return true;
 
 	case DSO_BINARY_TYPE__NOT_FOUND:
@@ -1565,10 +1567,14 @@ int dso__load(struct dso *dso, struct map *map)
 		struct symsrc *ss = &ss_[ss_pos];
 		bool next_slot = false;
 		bool is_reg;
+		bool nsexit;
 		int sirc;
 
 		enum dso_binary_type symtab_type = binary_type_symtab[i];
 
+		nsexit = (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE ||
+		    symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO);
+
 		if (!dso__is_compatible_symtab_type(dso, kmod, symtab_type))
 			continue;
 
@@ -1576,13 +1582,13 @@ int dso__load(struct dso *dso, struct map *map)
 						   root_dir, name, PATH_MAX))
 			continue;
 
-		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		if (nsexit)
 			nsinfo__mountns_exit(&nsc);
 
 		is_reg = is_regular_file(name);
 		sirc = symsrc__init(ss, dso, name, symtab_type);
 
-		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		if (nsexit)
 			nsinfo__mountns_enter(dso->nsinfo, &nsc);
 
 		if (!is_reg || sirc < 0) {
@@ -1724,7 +1730,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map)
 	}
 
 	if (!symbol_conf.ignore_vmlinux_buildid)
-		filename = dso__build_id_filename(dso, NULL, 0);
+		filename = dso__build_id_filename(dso, NULL, 0, false);
 	if (filename != NULL) {
 		err = dso__load_vmlinux(dso, map, filename, true);
 		if (err > 0)
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace Krister Johansen
@ 2017-07-06 19:41           ` Arnaldo Carvalho de Melo
  2017-07-07 19:36             ` Krister Johansen
  2017-07-20  8:48           ` [tip:perf/core] perf symbols: Find " tip-bot for Krister Johansen
  1 sibling, 1 reply; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-06 19:41 UTC (permalink / raw)
  To: Krister Johansen
  Cc: Brendan Gregg, Peter Zijlstra, Ingo Molnar, Alexander Shishkin,
	linux-kernel, Thomas-Mich Richter

Em Wed, Jul 05, 2017 at 06:48:08PM -0700, Krister Johansen escreveu:
> 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.

I was trying to find a way to test after applying each of the patches in
this series, when it ocurred to me that if a process that appears on a
perf.data file has exit, how can we access /proc/%ITS_PID/something?

This can be used for synthesizing records at 'perf record' start, but
how to get that on a different machine, different arch, say?

We would use what is in PERF_RECORD_NAMESPACES? How?

What we have there is:

        /*
         * struct {
         *      struct perf_event_header        header;
         *      u32                             pid;
         *      u32                             tid;
         *      u64                             nr_namespaces;
         *      { u64                           dev, inode; } [nr_namespaces];
         *      struct sample_id                sample_id;
         * };
         */
        PERF_RECORD_NAMESPACES                  = 16,

So how would we use setns with that?

I must be missing something...

- Arnaldo
 
> Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
> ---
>  tools/perf/util/dso.c        |   1 +
>  tools/perf/util/dso.h        |   2 +
>  tools/perf/util/map.c        |   2 +
>  tools/perf/util/namespaces.c | 127 +++++++++++++++++++++++++++++++++++++++++++
>  tools/perf/util/namespaces.h |  33 +++++++++++
>  tools/perf/util/symbol.c     |  11 ++++
>  tools/perf/util/thread.c     |   3 +
>  tools/perf/util/thread.h     |   1 +
>  8 files changed, 180 insertions(+)
> 
> diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
> index 4e7ab61..beda40e 100644
> --- a/tools/perf/util/dso.c
> +++ b/tools/perf/util/dso.c
> @@ -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);
>  }
> diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
> index bd061ba..78ec637 100644
> --- a/tools/perf/util/dso.h
> +++ b/tools/perf/util/dso.h
> @@ -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];
>  };
> diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
> index 2179b2d..5dc60ca 100644
> --- a/tools/perf/util/map.c
> +++ b/tools/perf/util/map.c
> @@ -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;
> diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
> index 67dcbcc..bcc6bb1 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;
> +	}
> +}
> diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
> index 468f1e9..b20f6ea 100644
> --- a/tools/perf/util/namespaces.h
> +++ b/tools/perf/util/namespaces.h
> @@ -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 */
> diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
> index e7a98db..60a9eaa 100644
> --- a/tools/perf/util/symbol.c
> +++ b/tools/perf/util/symbol.c
> @@ -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 @@ int dso__load(struct dso *dso, struct map *map)
>  out:
>  	dso__set_loaded(dso, map->type);
>  	pthread_mutex_unlock(&dso->lock);
> +	nsinfo__mountns_exit(&nsc);
>  
>  	return ret;
>  }
> diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
> index 378c418..aee9a42 100644
> --- a/tools/perf/util/thread.c
> +++ b/tools/perf/util/thread.c
> @@ -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);
>  }
> diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
> index 4eb849e..cb1a5dd 100644
> --- a/tools/perf/util/thread.h
> +++ b/tools/perf/util/thread.h
> @@ -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;
> -- 
> 2.7.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-06 19:41           ` Arnaldo Carvalho de Melo
@ 2017-07-07 19:36             ` Krister Johansen
  2017-07-10  6:17               ` Thomas-Mich Richter
  0 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-07 19:36 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Krister Johansen, Brendan Gregg, Peter Zijlstra, Ingo Molnar,
	Alexander Shishkin, linux-kernel, Thomas-Mich Richter

On Thu, Jul 06, 2017 at 04:41:30PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Wed, Jul 05, 2017 at 06:48:08PM -0700, Krister Johansen escreveu:
> > 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.
> 
> I was trying to find a way to test after applying each of the patches in
> this series, when it ocurred to me that if a process that appears on a
> perf.data file has exit, how can we access /proc/%ITS_PID/something?

You're correct.  We can't access /proc/<PID>/whatever once the process
has exited.  That was the impeteus for patches 4 and 6, which allow us
to capture the binary (and debuginfo, if it exists) into the buildid
cache so that if we do have a trace that exists after a process or
container exists, we'll still be able to resolve some of the symbols.

-K

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-07 19:36             ` Krister Johansen
@ 2017-07-10  6:17               ` Thomas-Mich Richter
  2017-07-10 22:39                 ` Krister Johansen
  0 siblings, 1 reply; 42+ messages in thread
From: Thomas-Mich Richter @ 2017-07-10  6:17 UTC (permalink / raw)
  To: Krister Johansen, Arnaldo Carvalho de Melo
  Cc: Brendan Gregg, Peter Zijlstra, Ingo Molnar, Alexander Shishkin,
	linux-kernel

On 07/07/2017 09:36 PM, Krister Johansen wrote:
> On Thu, Jul 06, 2017 at 04:41:30PM -0300, Arnaldo Carvalho de Melo wrote:
>> Em Wed, Jul 05, 2017 at 06:48:08PM -0700, Krister Johansen escreveu:
>>> 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.
>>
>> I was trying to find a way to test after applying each of the patches in
>> this series, when it ocurred to me that if a process that appears on a
>> perf.data file has exit, how can we access /proc/%ITS_PID/something?
> 
> You're correct.  We can't access /proc/<PID>/whatever once the process
> has exited.  That was the impeteus for patches 4 and 6, which allow us
> to capture the binary (and debuginfo, if it exists) into the buildid
> cache so that if we do have a trace that exists after a process or
> container exists, we'll still be able to resolve some of the symbols.
> 
> -K
> 

Any ideas on how to extend this to be able to resolve symbols after
the process/container exited?
I believe it boils down on how to interpret the mnt inode number in the
PERF_RECORD_NAMESPACE record...
Can this be done post-mortem? Maybe the PERF_RECORD_NAMESPACE record
has to contain more data than just the inode number?

-- 
Thomas Richter, Dept 3303, IBM LTC Boeblingen Germany
--
Vorsitzende des Aufsichtsrats: Martina Koederitz 
Geschäftsführung: Dirk Wittkopp
Sitz der Gesellschaft: Böblingen / Registergericht: Amtsgericht Stuttgart, HRB 243294

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-10  6:17               ` Thomas-Mich Richter
@ 2017-07-10 22:39                 ` Krister Johansen
  2017-07-10 22:52                   ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-10 22:39 UTC (permalink / raw)
  To: Thomas-Mich Richter
  Cc: Krister Johansen, Arnaldo Carvalho de Melo, Brendan Gregg,
	Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel

On Mon, Jul 10, 2017 at 08:17:00AM +0200, Thomas-Mich Richter wrote:
> On 07/07/2017 09:36 PM, Krister Johansen wrote:
> > On Thu, Jul 06, 2017 at 04:41:30PM -0300, Arnaldo Carvalho de Melo wrote:
> >> Em Wed, Jul 05, 2017 at 06:48:08PM -0700, Krister Johansen escreveu:
> >>> 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.
> >>
> >> I was trying to find a way to test after applying each of the patches in
> >> this series, when it ocurred to me that if a process that appears on a
> >> perf.data file has exit, how can we access /proc/%ITS_PID/something?
> > 
> > You're correct.  We can't access /proc/<PID>/whatever once the process
> > has exited.  That was the impeteus for patches 4 and 6, which allow us
> > to capture the binary (and debuginfo, if it exists) into the buildid
> > cache so that if we do have a trace that exists after a process or
> > container exists, we'll still be able to resolve some of the symbols.
> 
> Any ideas on how to extend this to be able to resolve symbols after
> the process/container exited?
> I believe it boils down on how to interpret the mnt inode number in the
> PERF_RECORD_NAMESPACE record...
> Can this be done post-mortem? Maybe the PERF_RECORD_NAMESPACE record
> has to contain more data than just the inode number?

I think we're talking past one another.  If the container exits then the
inode numbers that identify mount namespace are referring to something
that is no longer valid.  There's no mount namespace to enter in order
to locate the binary objects.  They may be on a volume that's no longer
mounted.

I have a pair of patches in the existing set that copies the binary
objects into the buildid cache.  This lets you resolve the symbols after
the container has exited, provided that you recorded the buildids during
the trace.

If you apply all the patches in this set, you should be able to generate
traces that you can look at with script or report even after the process
has exited.  I've been able to do it in my tests, at least.

-K

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-10 22:39                 ` Krister Johansen
@ 2017-07-10 22:52                   ` Arnaldo Carvalho de Melo
  2017-07-10 23:29                     ` Krister Johansen
  0 siblings, 1 reply; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-10 22:52 UTC (permalink / raw)
  To: Krister Johansen
  Cc: Thomas-Mich Richter, Brendan Gregg, Peter Zijlstra, Ingo Molnar,
	Alexander Shishkin, linux-kernel

Em Mon, Jul 10, 2017 at 03:39:25PM -0700, Krister Johansen escreveu:
> On Mon, Jul 10, 2017 at 08:17:00AM +0200, Thomas-Mich Richter wrote:
> > On 07/07/2017 09:36 PM, Krister Johansen wrote:
> > > On Thu, Jul 06, 2017 at 04:41:30PM -0300, Arnaldo Carvalho de Melo wrote:
> > >> Em Wed, Jul 05, 2017 at 06:48:08PM -0700, Krister Johansen escreveu:
> > >>> 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.
> > >>
> > >> I was trying to find a way to test after applying each of the patches in
> > >> this series, when it ocurred to me that if a process that appears on a
> > >> perf.data file has exit, how can we access /proc/%ITS_PID/something?
> > > 
> > > You're correct.  We can't access /proc/<PID>/whatever once the process
> > > has exited.  That was the impeteus for patches 4 and 6, which allow us
> > > to capture the binary (and debuginfo, if it exists) into the buildid
> > > cache so that if we do have a trace that exists after a process or
> > > container exists, we'll still be able to resolve some of the symbols.

> > Any ideas on how to extend this to be able to resolve symbols after
> > the process/container exited?
> > I believe it boils down on how to interpret the mnt inode number in the
> > PERF_RECORD_NAMESPACE record...
> > Can this be done post-mortem? Maybe the PERF_RECORD_NAMESPACE record
> > has to contain more data than just the inode number?
 
> I think we're talking past one another.  If the container exits then the
> inode numbers that identify mount namespace are referring to something
> that is no longer valid.  There's no mount namespace to enter in order
> to locate the binary objects.  They may be on a volume that's no longer
> mounted.
 
> I have a pair of patches in the existing set that copies the binary
> objects into the buildid cache.  This lets you resolve the symbols after
> the container has exited, provided that you recorded the buildids during
> the trace.
 
> If you apply all the patches in this set, you should be able to generate
> traces that you can look at with script or report even after the process
> has exited.  I've been able to do it in my tests, at least.

I will work on testing them soon, I just wanted this discussion to take
place, what you did seems to be the best we can do with the existing
kernel infrastructure, and is a clear advance, so we need to test and
merge it.

Getting the build-ids for the binaries is the key here, then its just a
matter of populating a database where to get the matching binaries, we
wouldn't need even to copy the actual binaries at record time.

- Arnaldo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-10 22:52                   ` Arnaldo Carvalho de Melo
@ 2017-07-10 23:29                     ` Krister Johansen
  2017-07-11 12:51                       ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 42+ messages in thread
From: Krister Johansen @ 2017-07-10 23:29 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Krister Johansen, Thomas-Mich Richter, Brendan Gregg,
	Peter Zijlstra, Ingo Molnar, Alexander Shishkin, linux-kernel

On Mon, Jul 10, 2017 at 07:52:49PM -0300, Arnaldo Carvalho de Melo wrote:
> Em Mon, Jul 10, 2017 at 03:39:25PM -0700, Krister Johansen escreveu:
> > On Mon, Jul 10, 2017 at 08:17:00AM +0200, Thomas-Mich Richter wrote:
> > > On 07/07/2017 09:36 PM, Krister Johansen wrote:
> > > > On Thu, Jul 06, 2017 at 04:41:30PM -0300, Arnaldo Carvalho de Melo wrote:
> > > >> Em Wed, Jul 05, 2017 at 06:48:08PM -0700, Krister Johansen escreveu:
> > > >>> 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.
> > > >>
> > > >> I was trying to find a way to test after applying each of the patches in
> > > >> this series, when it ocurred to me that if a process that appears on a
> > > >> perf.data file has exit, how can we access /proc/%ITS_PID/something?
> > > > 
> > > > You're correct.  We can't access /proc/<PID>/whatever once the process
> > > > has exited.  That was the impeteus for patches 4 and 6, which allow us
> > > > to capture the binary (and debuginfo, if it exists) into the buildid
> > > > cache so that if we do have a trace that exists after a process or
> > > > container exists, we'll still be able to resolve some of the symbols.
> 
> > > Any ideas on how to extend this to be able to resolve symbols after
> > > the process/container exited?
> > > I believe it boils down on how to interpret the mnt inode number in the
> > > PERF_RECORD_NAMESPACE record...
> > > Can this be done post-mortem? Maybe the PERF_RECORD_NAMESPACE record
> > > has to contain more data than just the inode number?
>  
> > I think we're talking past one another.  If the container exits then the
> > inode numbers that identify mount namespace are referring to something
> > that is no longer valid.  There's no mount namespace to enter in order
> > to locate the binary objects.  They may be on a volume that's no longer
> > mounted.
>  
> > I have a pair of patches in the existing set that copies the binary
> > objects into the buildid cache.  This lets you resolve the symbols after
> > the container has exited, provided that you recorded the buildids during
> > the trace.
>  
> > If you apply all the patches in this set, you should be able to generate
> > traces that you can look at with script or report even after the process
> > has exited.  I've been able to do it in my tests, at least.
> 
> I will work on testing them soon, I just wanted this discussion to take
> place, what you did seems to be the best we can do with the existing
> kernel infrastructure, and is a clear advance, so we need to test and
> merge it.

Happy to have the discussion. Aplologies if having the patches
iteratively add to one another isn't the best way to have this reviewed
and understood.  If you just apply the first few, you don't get the
support to pull these into the build-id cache.

> Getting the build-ids for the binaries is the key here, then its just a
> matter of populating a database where to get the matching binaries, we
> wouldn't need even to copy the actual binaries at record time.

Unfortunately, it's not sufficient to save the path to the target binary
because it's possible that after the container exits, and the namespace
is destroyed, there may be no path that describes to the host how to
access the files in the container.  There are two different interactions
here that frustrate this:

1. Containers run under a pivoted root, so the containers view of the
path may be different from the host's view of the path.  E.g. /usr/bin/node
in the container may actually be /var/container_a/root/usr/bin/node, or
something like that.  However, see #2.

2. It's also entirely possible for a container to have mounted a
filesystem that's not accessible or mounted from the host.  If, for
example, you're using docker with the direct-lvm storage driver, then
your storage device may be mounted in the vfs attached to the container,
but have no mount in the host's vfs.  In a situation like this, once the
container exits, the that lvm filesystem is unmounted.  In order to
access the files in that container, you basically need to setns(2) into
the container's mount namespace and look up the files using the a path
that resolves in the mount namespace of perf's target.

-K

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-10 23:29                     ` Krister Johansen
@ 2017-07-11 12:51                       ` Arnaldo Carvalho de Melo
  2017-07-11 17:15                         ` Krister Johansen
  0 siblings, 1 reply; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-11 12:51 UTC (permalink / raw)
  To: Krister Johansen
  Cc: Thomas-Mich Richter, Brendan Gregg, Peter Zijlstra, Ingo Molnar,
	Alexander Shishkin, linux-kernel

Em Mon, Jul 10, 2017 at 04:29:43PM -0700, Krister Johansen escreveu:
> On Mon, Jul 10, 2017 at 07:52:49PM -0300, Arnaldo Carvalho de Melo wrote:
> > I will work on testing them soon, I just wanted this discussion to take
> > place, what you did seems to be the best we can do with the existing
> > kernel infrastructure, and is a clear advance, so we need to test and
> > merge it.
 
> Happy to have the discussion. Aplologies if having the patches
> iteratively add to one another isn't the best way to have this reviewed
> and understood.  If you just apply the first few, you don't get the
> support to pull these into the build-id cache.
 
> > Getting the build-ids for the binaries is the key here, then its just a
> > matter of populating a database where to get the matching binaries, we
> > wouldn't need even to copy the actual binaries at record time.
 
> Unfortunately, it's not sufficient to save the path to the target binary
> because it's possible that after the container exits, and the namespace

The path is not that important, as "/usr/lib64/libc-2.24.so" is not
enough to uniquely identify a binary, for instance, here in this machine
I have:

[root@jouet ~]# ls -la /root/.debug/usr/lib64/libc-2.24.so/
total 16
drwxr-xr-x.  4 root root 4096 Jun 29 15:46 .
drwxr-xr-x. 40 root root 4096 Jul  7 12:28 ..
drwxr-xr-x.  2 root root 4096 Jun 29 15:46 1c80f527d122e71f3dd3bd7d7f8a00a80143ae53
drwxr-xr-x.  2 root root 4096 Jun 23 10:43 b0fa2afea4d9239b66a0a260cbaceb1b9532299a
[root@jouet ~]#

[root@jouet ~]# file /root/.debug/usr/lib64/libc-2.24.so/*/elf 
/root/.debug/usr/lib64/libc-2.24.so/1c80f527d122e71f3dd3bd7d7f8a00a80143ae53/elf: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=1c80f527d122e71f3dd3bd7d7f8a00a80143ae53, for GNU/Linux 2.6.32, not stripped
/root/.debug/usr/lib64/libc-2.24.so/b0fa2afea4d9239b66a0a260cbaceb1b9532299a/elf: ELF 64-bit LSB shared object, x86-64, version 1 (GNU/Linux), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=b0fa2afea4d9239b66a0a260cbaceb1b9532299a, for GNU/Linux 2.6.32, not stripped
[root@jouet ~]# o

[root@jouet ~]# readelf -sW /root/.debug/usr/lib64/libc-2.24.so/1c80f527d122e71f3dd3bd7d7f8a00a80143ae53/elf > /tmp/a
[root@jouet ~]# readelf -sW /root/.debug/usr/lib64/libc-2.24.so/b0fa2afea4d9239b66a0a260cbaceb1b9532299a/elf > /tmp/b
[root@jouet ~]# diff -u /tmp/a /tmp/b | wc -l
16398
[root@jouet ~]# diff -u /tmp/a /tmp/b | head
@@ -13,298 +13,298 @@
      9: 0000000000000000     0 OBJECT  GLOBAL DEFAULT  UND _dl_argv@GLIBC_PRIVATE (27)
     10: 000000000009fbd0    29 FUNC    GLOBAL DEFAULT   13 __strspn_c1@GLIBC_2.2.5
     11: 0000000000072690   333 FUNC    GLOBAL DEFAULT   13 putwchar@@GLIBC_2.2.5
-    12: 00000000001195c0    19 FUNC    GLOBAL DEFAULT   13 __gethostname_chk@@GLIBC_2.4
+    12: 0000000000119630    19 FUNC    GLOBAL DEFAULT   13 __gethostname_chk@@GLIBC_2.4
     13: 000000000009fbf0    37 FUNC    GLOBAL DEFAULT   13 __strspn_c2@GLIBC_2.2.5
-    14: 0000000000132e80   192 FUNC    GLOBAL DEFAULT   13 setrpcent@@GLIBC_2.2.5
[root@jouet ~]# 

We need to as soon as possible to get the content based unique
identifier for a binary, then try to use just that, not the pathname.

> is destroyed, there may be no path that describes to the host how to
> access the files in the container.  There are two different interactions

Right, we need to use the build-id and look it up in a database
populated somehow.

perf right now, by default, collects the build-ids in a table, at the
end of the recording session, trying not to disrupt the monitored
workload by not processing anything, just reading from the buffers and
dumping to a file.

It will also try to populate the build-id, trying first to make a
hardlink and copying it if it fails.

If we can get the build-id at the time of the mmap(binary), as part of
the loading of binaries, that would be ideal, as we're touching the file
headers anyway and the build-id is a small enough cookie.

But again, we should first try to do as far as we can with the
infrastructure we have in the kernel and tooling libraries, lots of
workloads will be serviced just fine with that.

> here that frustrate this:
> 
> 1. Containers run under a pivoted root, so the containers view of the
> path may be different from the host's view of the path.  E.g. /usr/bin/node
> in the container may actually be /var/container_a/root/usr/bin/node, or
> something like that.  However, see #2.
> 
> 2. It's also entirely possible for a container to have mounted a
> filesystem that's not accessible or mounted from the host.  If, for
> example, you're using docker with the direct-lvm storage driver, then
> your storage device may be mounted in the vfs attached to the container,
> but have no mount in the host's vfs.  In a situation like this, once the
> container exits, the that lvm filesystem is unmounted.  In order to
> access the files in that container, you basically need to setns(2) into
> the container's mount namespace and look up the files using the a path
> that resolves in the mount namespace of perf's target.

That all frustrates accessing the binary via a pathname, agreed.

- Arnaldo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace
  2017-07-11 12:51                       ` Arnaldo Carvalho de Melo
@ 2017-07-11 17:15                         ` Krister Johansen
  0 siblings, 0 replies; 42+ messages in thread
From: Krister Johansen @ 2017-07-11 17:15 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo
  Cc: Thomas-Mich Richter, Brendan Gregg, Peter Zijlstra, Ingo Molnar,
	Alexander Shishkin, linux-kernel

On Tue, Jul 11, 2017 at 09:51:16AM -0300, Arnaldo Carvalho de Melo wrote:
> Right, we need to use the build-id and look it up in a database
> populated somehow.
> 
> perf right now, by default, collects the build-ids in a table, at the
> end of the recording session, trying not to disrupt the monitored
> workload by not processing anything, just reading from the buffers and
> dumping to a file.
> 
> It will also try to populate the build-id, trying first to make a
> hardlink and copying it if it fails.
> 
> If we can get the build-id at the time of the mmap(binary), as part of
> the loading of binaries, that would be ideal, as we're touching the file
> headers anyway and the build-id is a small enough cookie.
> 
> But again, we should first try to do as far as we can with the
> infrastructure we have in the kernel and tooling libraries, lots of
> workloads will be serviced just fine with that.

Sorry, I was slow to pick up on what you're saying here.  I agree that
getting the build-id delivered in an event from the kernel would
accelerate the process of determining whether you have or need to cache
a binary in the build-id cache.  Without such a modification, perf has
to look at each binary to determine whether the build ids match.  If we
got the build-id in the event payload, then we only need to look to see
if the build-id is cached.  If it's not, then we can undertake the more
complicated lookup path.

That would be an improvement over what we can do today.

-K

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [PATCH] perf probe: Fix build failure for get_target_map()
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 3/6] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
@ 2017-07-17 10:32           ` Ravi Bangoria
  2017-07-17 13:11             ` Arnaldo Carvalho de Melo
  2017-07-20  8:48           ` [tip:perf/core] perf probe: Allow placing uprobes in alternate namespaces tip-bot for Krister Johansen
  1 sibling, 1 reply; 42+ messages in thread
From: Ravi Bangoria @ 2017-07-17 10:32 UTC (permalink / raw)
  To: acme, kjlx; +Cc: linux-kernel, Ravi Bangoria

Commit 801bc8193463 ("perf probe: Allow placing uprobes in
alternate namespaces.") is causing a build failure on powerpc:

  error: incompatible type for argument 2 of 'get_target_map'
  map = get_target_map(pev->target, pev->uprobes);
                                    ^~~

Fix it by changing parameters of get_target_map().

Fixes: 801bc8193463 ("perf probe: Allow placing uprobes in alternate namespaces.")
Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
---
 tools/perf/arch/powerpc/util/sym-handling.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index bf9a259..9c4e23d 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -126,7 +126,7 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
 	struct rb_node *tmp;
 	int i = 0;
 
-	map = get_target_map(pev->target, pev->uprobes);
+	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
 	if (!map || map__load(map) < 0)
 		return;
 
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH] perf probe: Fix build failure for get_target_map()
  2017-07-17 10:32           ` [PATCH] perf probe: Fix build failure for get_target_map() Ravi Bangoria
@ 2017-07-17 13:11             ` Arnaldo Carvalho de Melo
  2017-07-18  9:19               ` Michael Ellerman
  0 siblings, 1 reply; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-17 13:11 UTC (permalink / raw)
  To: Ravi Bangoria; +Cc: kjlx, linux-kernel

Em Mon, Jul 17, 2017 at 04:02:22PM +0530, Ravi Bangoria escreveu:
> Commit 801bc8193463 ("perf probe: Allow placing uprobes in
> alternate namespaces.") is causing a build failure on powerpc:
> 
>   error: incompatible type for argument 2 of 'get_target_map'
>   map = get_target_map(pev->target, pev->uprobes);
>                                     ^~~
> 
> Fix it by changing parameters of get_target_map().

Thanks, since this hasn't made it to Ingo I'll fold this into the patch
where the problem was introduced, adding credits to you, leaving a Link
to this message, so that we don't lose bisection in this area on
powerpc.

- Arnaldo
 
> Fixes: 801bc8193463 ("perf probe: Allow placing uprobes in alternate namespaces.")
> Signed-off-by: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
> ---
>  tools/perf/arch/powerpc/util/sym-handling.c | 2 +-
>  1 file changed, 1 insertion(+), 1 deletion(-)
> 
> diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
> index bf9a259..9c4e23d 100644
> --- a/tools/perf/arch/powerpc/util/sym-handling.c
> +++ b/tools/perf/arch/powerpc/util/sym-handling.c
> @@ -126,7 +126,7 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
>  	struct rb_node *tmp;
>  	int i = 0;
>  
> -	map = get_target_map(pev->target, pev->uprobes);
> +	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
>  	if (!map || map__load(map) < 0)
>  		return;
>  
> -- 
> 2.1.4

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] perf probe: Fix build failure for get_target_map()
  2017-07-17 13:11             ` Arnaldo Carvalho de Melo
@ 2017-07-18  9:19               ` Michael Ellerman
  2017-07-18 15:45                 ` Arnaldo Carvalho de Melo
  0 siblings, 1 reply; 42+ messages in thread
From: Michael Ellerman @ 2017-07-18  9:19 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo, Ravi Bangoria; +Cc: kjlx, linux-kernel

Arnaldo Carvalho de Melo <acme@kernel.org> writes:

> Em Mon, Jul 17, 2017 at 04:02:22PM +0530, Ravi Bangoria escreveu:
>> Commit 801bc8193463 ("perf probe: Allow placing uprobes in
>> alternate namespaces.") is causing a build failure on powerpc:
>> 
>>   error: incompatible type for argument 2 of 'get_target_map'
>>   map = get_target_map(pev->target, pev->uprobes);
>>                                     ^~~
>> 
>> Fix it by changing parameters of get_target_map().
>
> Thanks, since this hasn't made it to Ingo I'll fold this into the patch
> where the problem was introduced, adding credits to you, leaving a Link
> to this message, so that we don't lose bisection in this area on
> powerpc.

Thanks.

I just bisected to this commit too - should have checked the list first! :)

cheers

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] perf probe: Fix build failure for get_target_map()
  2017-07-18  9:19               ` Michael Ellerman
@ 2017-07-18 15:45                 ` Arnaldo Carvalho de Melo
  2017-07-19  5:58                   ` Michael Ellerman
  0 siblings, 1 reply; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-18 15:45 UTC (permalink / raw)
  To: Michael Ellerman; +Cc: Ravi Bangoria, kjlx, linux-kernel

Em Tue, Jul 18, 2017 at 07:19:45PM +1000, Michael Ellerman escreveu:
> Arnaldo Carvalho de Melo <acme@kernel.org> writes:
> 
> > Em Mon, Jul 17, 2017 at 04:02:22PM +0530, Ravi Bangoria escreveu:
> >> Commit 801bc8193463 ("perf probe: Allow placing uprobes in
> >> alternate namespaces.") is causing a build failure on powerpc:
> >> 
> >>   error: incompatible type for argument 2 of 'get_target_map'
> >>   map = get_target_map(pev->target, pev->uprobes);
> >>                                     ^~~
> >> 
> >> Fix it by changing parameters of get_target_map().
> >
> > Thanks, since this hasn't made it to Ingo I'll fold this into the patch
> > where the problem was introduced, adding credits to you, leaving a Link
> > to this message, so that we don't lose bisection in this area on
> > powerpc.
> 
> Thanks.
> 
> I just bisected to this commit too - should have checked the list first! :)

:-)

I just re-ammended this cset to cover the !HAVE_DWARF_SUPPORT build as
well, just re-force-pushed acme/perf/core with this fix.

- Arnaldo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* Re: [PATCH] perf probe: Fix build failure for get_target_map()
  2017-07-18 15:45                 ` Arnaldo Carvalho de Melo
@ 2017-07-19  5:58                   ` Michael Ellerman
  0 siblings, 0 replies; 42+ messages in thread
From: Michael Ellerman @ 2017-07-19  5:58 UTC (permalink / raw)
  To: Arnaldo Carvalho de Melo; +Cc: Ravi Bangoria, kjlx, linux-kernel

Arnaldo Carvalho de Melo <acme@kernel.org> writes:

> Em Tue, Jul 18, 2017 at 07:19:45PM +1000, Michael Ellerman escreveu:
>> Arnaldo Carvalho de Melo <acme@kernel.org> writes:
>> 
>> > Em Mon, Jul 17, 2017 at 04:02:22PM +0530, Ravi Bangoria escreveu:
>> >> Commit 801bc8193463 ("perf probe: Allow placing uprobes in
>> >> alternate namespaces.") is causing a build failure on powerpc:
>> >> 
>> >>   error: incompatible type for argument 2 of 'get_target_map'
>> >>   map = get_target_map(pev->target, pev->uprobes);
>> >>                                     ^~~
>> >> 
>> >> Fix it by changing parameters of get_target_map().
>> >
>> > Thanks, since this hasn't made it to Ingo I'll fold this into the patch
>> > where the problem was introduced, adding credits to you, leaving a Link
>> > to this message, so that we don't lose bisection in this area on
>> > powerpc.
>> 
>> Thanks.
>> 
>> I just bisected to this commit too - should have checked the list first! :)
>
> :-)
>
> I just re-ammended this cset to cover the !HAVE_DWARF_SUPPORT build as
> well, just re-force-pushed acme/perf/core with this fix.

Thanks, all green here.

cheers

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [tip:perf/core] perf symbols: Find symbols in different mount namespace
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace Krister Johansen
  2017-07-06 19:41           ` Arnaldo Carvalho de Melo
@ 2017-07-20  8:48           ` tip-bot for Krister Johansen
  1 sibling, 0 replies; 42+ messages in thread
From: tip-bot for Krister Johansen @ 2017-07-20  8:48 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: kjlx, tglx, peterz, linux-kernel, hpa, alexander.shishkin, acme,
	tmricht, brendan.d.gregg, mingo

Commit-ID:  843ff37bb59edbe51d64e77ba1b3245a15a4dd9f
Gitweb:     http://git.kernel.org/tip/843ff37bb59edbe51d64e77ba1b3245a15a4dd9f
Author:     Krister Johansen <kjlx@templeofstupid.com>
AuthorDate: Wed, 5 Jul 2017 18:48:08 -0700
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Tue, 18 Jul 2017 23:14:09 -0300

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>
---
 tools/perf/util/dso.c        |   1 +
 tools/perf/util/dso.h        |   2 +
 tools/perf/util/map.c        |   2 +
 tools/perf/util/namespaces.c | 127 +++++++++++++++++++++++++++++++++++++++++++
 tools/perf/util/namespaces.h |  33 +++++++++++
 tools/perf/util/symbol.c     |  11 ++++
 tools/perf/util/thread.c     |   3 +
 tools/perf/util/thread.h     |   1 +
 8 files changed, 180 insertions(+)

diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index 4e7ab61..beda40e 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -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);
 }
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index bd061ba..78ec637 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -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];
 };
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 2179b2d..5dc60ca 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -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;
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index 67dcbcc..bcc6bb1 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;
+	}
+}
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 468f1e9..b20f6ea 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -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 */
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index e7a98db..60a9eaa 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -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;
 }
diff --git a/tools/perf/util/thread.c b/tools/perf/util/thread.c
index 378c418..aee9a42 100644
--- a/tools/perf/util/thread.c
+++ b/tools/perf/util/thread.c
@@ -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);
 }
diff --git a/tools/perf/util/thread.h b/tools/perf/util/thread.h
index 4eb849e..cb1a5dd 100644
--- a/tools/perf/util/thread.h
+++ b/tools/perf/util/thread.h
@@ -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;

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [tip:perf/core] perf maps: Lookup maps in both intitial mountns and inner mountns.
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 2/6] perf maps: lookup maps in both intitial mountns and inner mountns Krister Johansen
@ 2017-07-20  8:48           ` tip-bot for Krister Johansen
  0 siblings, 0 replies; 42+ messages in thread
From: tip-bot for Krister Johansen @ 2017-07-20  8:48 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: brendan.d.gregg, alexander.shishkin, mingo, tglx, peterz,
	linux-kernel, hpa, acme, kjlx, tmricht

Commit-ID:  bf2e710b3cb8445c052f2ff50b4875a2523bb279
Gitweb:     http://git.kernel.org/tip/bf2e710b3cb8445c052f2ff50b4875a2523bb279
Author:     Krister Johansen <kjlx@templeofstupid.com>
AuthorDate: Wed, 5 Jul 2017 18:48:09 -0700
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Tue, 18 Jul 2017 23:14:09 -0300

perf maps: Lookup maps in both intitial mountns and inner mountns.

If a process is in a mountns and has symbols in /tmp/perf-<pid>.map,
look first in the namespace using the tgid for the pidns that the
process might be in.  If the map isn't found there, try looking in the
mountns where perf is running, and use the tgid that's appropriate for
perf's pid namespace.  If all else fails, use the original pid.

This allows us to locate a symbol map file in the mount namespace, if it
was generated there.  However, we also try the tool's /tmp in case it's
there instead.

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-3-git-send-email-kjlx@templeofstupid.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/util/machine.c    |  4 +--
 tools/perf/util/map.c        | 23 +++++++++---
 tools/perf/util/map.h        |  2 +-
 tools/perf/util/namespaces.c | 83 ++++++++++++++++++++++++++++++++++++++++----
 tools/perf/util/namespaces.h |  5 ++-
 tools/perf/util/symbol.c     | 70 ++++++++++++++++++++++++++++++-------
 6 files changed, 160 insertions(+), 27 deletions(-)

diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 2e9eb6a..246b441 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -1392,7 +1392,7 @@ int machine__process_mmap2_event(struct machine *machine,
 
 	map = map__new(machine, event->mmap2.start,
 			event->mmap2.len, event->mmap2.pgoff,
-			event->mmap2.pid, event->mmap2.maj,
+			event->mmap2.maj,
 			event->mmap2.min, event->mmap2.ino,
 			event->mmap2.ino_generation,
 			event->mmap2.prot,
@@ -1450,7 +1450,7 @@ int machine__process_mmap_event(struct machine *machine, union perf_event *event
 
 	map = map__new(machine, event->mmap.start,
 			event->mmap.len, event->mmap.pgoff,
-			event->mmap.pid, 0, 0, 0, 0, 0, 0,
+			0, 0, 0, 0, 0, 0,
 			event->mmap.filename,
 			type, thread);
 
diff --git a/tools/perf/util/map.c b/tools/perf/util/map.c
index 5dc60ca..bdaa0a4 100644
--- a/tools/perf/util/map.c
+++ b/tools/perf/util/map.c
@@ -146,11 +146,13 @@ void map__init(struct map *map, enum map_type type,
 }
 
 struct map *map__new(struct machine *machine, u64 start, u64 len,
-		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
+		     u64 pgoff, u32 d_maj, u32 d_min, u64 ino,
 		     u64 ino_gen, u32 prot, u32 flags, char *filename,
 		     enum map_type type, struct thread *thread)
 {
 	struct map *map = malloc(sizeof(*map));
+	struct nsinfo *nsi = NULL;
+	struct nsinfo *nnsi;
 
 	if (map != NULL) {
 		char newfilename[PATH_MAX];
@@ -168,9 +170,11 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
 		map->ino_generation = ino_gen;
 		map->prot = prot;
 		map->flags = flags;
+		nsi = nsinfo__get(thread->nsinfo);
 
-		if ((anon || no_dso) && type == MAP__FUNCTION) {
-			snprintf(newfilename, sizeof(newfilename), "/tmp/perf-%d.map", pid);
+		if ((anon || no_dso) && nsi && type == MAP__FUNCTION) {
+			snprintf(newfilename, sizeof(newfilename),
+				 "/tmp/perf-%d.map", nsi->pid);
 			filename = newfilename;
 		}
 
@@ -180,6 +184,16 @@ struct map *map__new(struct machine *machine, u64 start, u64 len,
 		}
 
 		if (vdso) {
+			/* The vdso maps are always on the host and not the
+			 * container.  Ensure that we don't use setns to look
+			 * them up.
+			 */
+			nnsi = nsinfo__copy(nsi);
+			if (nnsi) {
+				nsinfo__put(nsi);
+				nnsi->need_setns = false;
+				nsi = nnsi;
+			}
 			pgoff = 0;
 			dso = machine__findnew_vdso(machine, thread);
 		} else
@@ -201,11 +215,12 @@ 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->nsinfo = nsi;
 		dso__put(dso);
 	}
 	return map;
 out_delete:
+	nsinfo__put(nsi);
 	free(map);
 	return NULL;
 }
diff --git a/tools/perf/util/map.h b/tools/perf/util/map.h
index f9e8ac8..73aacf7 100644
--- a/tools/perf/util/map.h
+++ b/tools/perf/util/map.h
@@ -141,7 +141,7 @@ struct thread;
 void map__init(struct map *map, enum map_type type,
 	       u64 start, u64 end, u64 pgoff, struct dso *dso);
 struct map *map__new(struct machine *machine, u64 start, u64 len,
-		     u64 pgoff, u32 pid, u32 d_maj, u32 d_min, u64 ino,
+		     u64 pgoff, u32 d_maj, u32 d_min, u64 ino,
 		     u64 ino_gen, u32 prot, u32 flags,
 		     char *filename, enum map_type type, struct thread *thread);
 struct map *map__new2(u64 start, struct dso *dso, enum map_type type);
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index bcc6bb1..fc5f398 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -40,18 +40,23 @@ void namespaces__free(struct namespaces *namespaces)
 	free(namespaces);
 }
 
-void nsinfo__init(struct nsinfo *nsi)
+int nsinfo__init(struct nsinfo *nsi)
 {
 	char oldns[PATH_MAX];
+	char spath[PATH_MAX];
 	char *newns = NULL;
+	char *statln = NULL;
 	struct stat old_stat;
 	struct stat new_stat;
+	FILE *f = NULL;
+	size_t linesz = 0;
+	int rv = -1;
 
 	if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
-		return;
+		return rv;
 
 	if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1)
-		return;
+		return rv;
 
 	if (stat(oldns, &old_stat) < 0)
 		goto out;
@@ -68,24 +73,89 @@ void nsinfo__init(struct nsinfo *nsi)
 		newns = NULL;
 	}
 
+	/* If we're dealing with a process that is in a different PID namespace,
+	 * attempt to work out the innermost tgid for the process.
+	 */
+	if (snprintf(spath, PATH_MAX, "/proc/%d/status", nsi->pid) >= PATH_MAX)
+		goto out;
+
+	f = fopen(spath, "r");
+	if (f == NULL)
+		goto out;
+
+	while (getline(&statln, &linesz, f) != -1) {
+		/* Use tgid if CONFIG_PID_NS is not defined. */
+		if (strstr(statln, "Tgid:") != NULL) {
+			nsi->tgid = (pid_t)strtol(strrchr(statln, '\t'),
+						     NULL, 10);
+			nsi->nstgid = nsi->tgid;
+		}
+
+		if (strstr(statln, "NStgid:") != NULL) {
+			nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'),
+						     NULL, 10);
+			break;
+		}
+	}
+	rv = 0;
+
 out:
+	if (f != NULL)
+		(void) fclose(f);
+	free(statln);
 	free(newns);
+	return rv;
 }
 
 struct nsinfo *nsinfo__new(pid_t pid)
 {
-	struct nsinfo *nsi = calloc(1, sizeof(*nsi));
+	struct nsinfo *nsi;
 
+	if (pid == 0)
+		return NULL;
+
+	nsi = calloc(1, sizeof(*nsi));
 	if (nsi != NULL) {
 		nsi->pid = pid;
+		nsi->tgid = pid;
+		nsi->nstgid = pid;
 		nsi->need_setns = false;
-		nsinfo__init(nsi);
+		/* Init may fail if the process exits while we're trying to look
+		 * at its proc information.  In that case, save the pid but
+		 * don't try to enter the namespace.
+		 */
+		if (nsinfo__init(nsi) == -1)
+			nsi->need_setns = false;
+
 		refcount_set(&nsi->refcnt, 1);
 	}
 
 	return nsi;
 }
 
+struct nsinfo *nsinfo__copy(struct nsinfo *nsi)
+{
+	struct nsinfo *nnsi;
+
+	nnsi = calloc(1, sizeof(*nnsi));
+	if (nnsi != NULL) {
+		nnsi->pid = nsi->pid;
+		nnsi->tgid = nsi->tgid;
+		nnsi->nstgid = nsi->nstgid;
+		nnsi->need_setns = nsi->need_setns;
+		if (nsi->mntns_path) {
+			nnsi->mntns_path = strdup(nsi->mntns_path);
+			if (!nnsi->mntns_path) {
+				free(nnsi);
+				return NULL;
+			}
+		}
+		refcount_set(&nnsi->refcnt, 1);
+	}
+
+	return nnsi;
+}
+
 void nsinfo__delete(struct nsinfo *nsi)
 {
 	zfree(&nsi->mntns_path);
@@ -105,7 +175,8 @@ void nsinfo__put(struct nsinfo *nsi)
 		nsinfo__delete(nsi);
 }
 
-void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc)
+void nsinfo__mountns_enter(struct nsinfo *nsi,
+				  struct nscookie *nc)
 {
 	char curpath[PATH_MAX];
 	int oldns = -1;
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index b20f6ea..f19aa41 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -26,6 +26,8 @@ void namespaces__free(struct namespaces *namespaces);
 
 struct nsinfo {
 	pid_t			pid;
+	pid_t			tgid;
+	pid_t			nstgid;
 	bool			need_setns;
 	char			*mntns_path;
 	refcount_t		refcnt;
@@ -36,8 +38,9 @@ struct nscookie {
 	int			newns;
 };
 
-void nsinfo__init(struct nsinfo *nsi);
+int nsinfo__init(struct nsinfo *nsi);
 struct nsinfo *nsinfo__new(pid_t pid);
+struct nsinfo *nsinfo__copy(struct nsinfo *nsi);
 void nsinfo__delete(struct nsinfo *nsi);
 
 struct nsinfo *nsinfo__get(struct nsinfo *nsi);
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 60a9eaa..21c97cc 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -19,7 +19,6 @@
 #include "strlist.h"
 #include "intlist.h"
 #include "namespaces.h"
-#include "vdso.h"
 #include "header.h"
 #include "path.h"
 #include "sane_ctype.h"
@@ -1327,14 +1326,15 @@ int dso__load_kallsyms(struct dso *dso, const char *filename,
 	return __dso__load_kallsyms(dso, filename, map, false);
 }
 
-static int dso__load_perf_map(struct dso *dso, struct map *map)
+static int dso__load_perf_map(const char *map_path, struct dso *dso,
+			      struct map *map)
 {
 	char *line = NULL;
 	size_t n;
 	FILE *file;
 	int nr_syms = 0;
 
-	file = fopen(dso->long_name, "r");
+	file = fopen(map_path, "r");
 	if (file == NULL)
 		goto out_failure;
 
@@ -1426,6 +1426,45 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
 	}
 }
 
+/* Checks for the existence of the perf-<pid>.map file in two different
+ * locations.  First, if the process is a separate mount namespace, check in
+ * that namespace using the pid of the innermost pid namespace.  If's not in a
+ * namespace, or the file can't be found there, try in the mount namespace of
+ * the tracing process using our view of its pid.
+ */
+static int dso__find_perf_map(char *filebuf, size_t bufsz,
+			      struct nsinfo **nsip)
+{
+	struct nscookie nsc;
+	struct nsinfo *nsi;
+	struct nsinfo *nnsi;
+	int rc = -1;
+
+	nsi = *nsip;
+
+	if (nsi->need_setns) {
+		snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nsi->nstgid);
+		nsinfo__mountns_enter(nsi, &nsc);
+		rc = access(filebuf, R_OK);
+		nsinfo__mountns_exit(&nsc);
+		if (rc == 0)
+			return rc;
+	}
+
+	nnsi = nsinfo__copy(nsi);
+	if (nnsi) {
+		nsinfo__put(nsi);
+
+		nnsi->need_setns = false;
+		snprintf(filebuf, bufsz, "/tmp/perf-%d.map", nnsi->tgid);
+		*nsip = nnsi;
+		rc = 0;
+	}
+
+	return rc;
+}
+
+
 int dso__load(struct dso *dso, struct map *map)
 {
 	char *name;
@@ -1437,18 +1476,23 @@ int dso__load(struct dso *dso, struct map *map)
 	struct symsrc ss_[2];
 	struct symsrc *syms_ss = NULL, *runtime_ss = NULL;
 	bool kmod;
+	bool perfmap;
 	unsigned char build_id[BUILD_ID_SIZE];
 	struct nscookie nsc;
+	char newmapname[PATH_MAX];
+	const char *map_path = dso->long_name;
+
+	perfmap = strncmp(dso->name, "/tmp/perf-", 10) == 0;
+	if (perfmap) {
+		if (dso->nsinfo && (dso__find_perf_map(newmapname,
+		    sizeof(newmapname), &dso->nsinfo) == 0)) {
+			map_path = newmapname;
+		}
+	}
 
 	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;
@@ -1471,19 +1515,19 @@ int dso__load(struct dso *dso, struct map *map)
 
 	dso->adjust_symbols = 0;
 
-	if (strncmp(dso->name, "/tmp/perf-", 10) == 0) {
+	if (perfmap) {
 		struct stat st;
 
-		if (lstat(dso->name, &st) < 0)
+		if (lstat(map_path, &st) < 0)
 			goto out;
 
 		if (!symbol_conf.force && st.st_uid && (st.st_uid != geteuid())) {
 			pr_warning("File %s not owned by current user or root, "
-				   "ignoring it (use -f to override).\n", dso->name);
+				   "ignoring it (use -f to override).\n", map_path);
 			goto out;
 		}
 
-		ret = dso__load_perf_map(dso, map);
+		ret = dso__load_perf_map(map_path, dso, map);
 		dso->symtab_type = ret > 0 ? DSO_BINARY_TYPE__JAVA_JIT :
 					     DSO_BINARY_TYPE__NOT_FOUND;
 		goto out;

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [tip:perf/core] perf probe: Allow placing uprobes in alternate namespaces.
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 3/6] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
  2017-07-17 10:32           ` [PATCH] perf probe: Fix build failure for get_target_map() Ravi Bangoria
@ 2017-07-20  8:48           ` tip-bot for Krister Johansen
  1 sibling, 0 replies; 42+ messages in thread
From: tip-bot for Krister Johansen @ 2017-07-20  8:48 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: mingo, linux-kernel, ravi.bangoria, tmricht, peterz,
	alexander.shishkin, brendan.d.gregg, hpa, acme, kjlx, tglx

Commit-ID:  544abd44c7064c8a58a6bd2073d757f6b91d98c5
Gitweb:     http://git.kernel.org/tip/544abd44c7064c8a58a6bd2073d757f6b91d98c5
Author:     Krister Johansen <kjlx@templeofstupid.com>
AuthorDate: Wed, 5 Jul 2017 18:48:10 -0700
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Tue, 18 Jul 2017 23:14:10 -0300

perf probe: Allow placing uprobes in alternate namespaces.

Teaches perf how to place a uprobe on a file that's in a different mount
namespace.  The user must add the probe using the --target-ns argument
to perf probe.  Once it has been placed, it may be recorded against
without further namespace-specific commands.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Ravi Bangoria <ravi.bangoria@linux.vnet.ibm.com>
[ PPC build fixed by Ravi: ]
Link: http://lkml.kernel.org/r/1500287542-6219-1-git-send-email-ravi.bangoria@linux.vnet.ibm.com
Cc: Thomas-Mich Richter <tmricht@linux.vnet.ibm.com>
[ Fix !HAVE_DWARF_SUPPORT build ]
Link: http://lkml.kernel.org/r/1499305693-1599-4-git-send-email-kjlx@templeofstupid.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/Documentation/perf-probe.txt     |  9 ++++
 tools/perf/arch/powerpc/util/sym-handling.c |  2 +-
 tools/perf/builtin-probe.c                  | 43 ++++++++++++++--
 tools/perf/util/namespaces.c                | 13 +++++
 tools/perf/util/namespaces.h                |  2 +
 tools/perf/util/probe-event.c               | 80 +++++++++++++++++++----------
 tools/perf/util/probe-event.h               | 10 ++--
 7 files changed, 125 insertions(+), 34 deletions(-)

diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index 165c2b1..a42aabc 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -130,6 +130,11 @@ OPTIONS
 --max-probes=NUM::
 	Set the maximum number of probe points for an event. Default is 128.
 
+--target-ns=PID:
+	Obtain mount namespace information from the target pid.  This is
+	used when creating a uprobe for a process that resides in a
+	different mount namespace from the perf(1) utility.
+
 -x::
 --exec=PATH::
 	Specify path to the executable or shared library file for user
@@ -264,6 +269,10 @@ Add probes at malloc() function on libc
 
  ./perf probe -x /lib/libc.so.6 malloc or ./perf probe /lib/libc.so.6 malloc
 
+Add a uprobe to a target process running in a different mount namespace
+
+ ./perf probe --target-ns <target pid> -x /lib64/libc.so.6 malloc
+
 SEE ALSO
 --------
 linkperf:perf-trace[1], linkperf:perf-record[1], linkperf:perf-buildid-cache[1]
diff --git a/tools/perf/arch/powerpc/util/sym-handling.c b/tools/perf/arch/powerpc/util/sym-handling.c
index bf9a259..9c4e23d 100644
--- a/tools/perf/arch/powerpc/util/sym-handling.c
+++ b/tools/perf/arch/powerpc/util/sym-handling.c
@@ -126,7 +126,7 @@ void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
 	struct rb_node *tmp;
 	int i = 0;
 
-	map = get_target_map(pev->target, pev->uprobes);
+	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
 	if (!map || map__load(map) < 0)
 		return;
 
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index cf9f9e9..3fb98d5 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -58,6 +58,7 @@ static struct {
 	struct line_range line_range;
 	char *target;
 	struct strfilter *filter;
+	struct nsinfo *nsi;
 } params;
 
 /* Parse an event definition. Note that any error must die. */
@@ -80,6 +81,9 @@ static int parse_probe_event(const char *str)
 		params.target_used = true;
 	}
 
+	if (params.nsi)
+		pev->nsi = nsinfo__get(params.nsi);
+
 	/* Parse a perf-probe command into event */
 	ret = parse_perf_probe_command(str, pev);
 	pr_debug("%d arguments\n", pev->nargs);
@@ -189,7 +193,7 @@ static int opt_set_target(const struct option *opt, const char *str,
 
 		/* Expand given path to absolute path, except for modulename */
 		if (params.uprobes || strchr(str, '/')) {
-			tmp = realpath(str, NULL);
+			tmp = nsinfo__realpath(str, params.nsi);
 			if (!tmp) {
 				pr_warning("Failed to get the absolute path of %s: %m\n", str);
 				return ret;
@@ -208,6 +212,34 @@ static int opt_set_target(const struct option *opt, const char *str,
 	return ret;
 }
 
+static int opt_set_target_ns(const struct option *opt __maybe_unused,
+			     const char *str, int unset __maybe_unused)
+{
+	int ret = -ENOENT;
+	pid_t ns_pid;
+	struct nsinfo *nsip;
+
+	if (str) {
+		errno = 0;
+		ns_pid = (pid_t)strtol(str, NULL, 10);
+		if (errno != 0) {
+			ret = -errno;
+			pr_warning("Failed to parse %s as a pid: %s\n", str,
+				   strerror(errno));
+			return ret;
+		}
+		nsip = nsinfo__new(ns_pid);
+		if (nsip && nsip->need_setns)
+			params.nsi = nsinfo__get(nsip);
+		nsinfo__put(nsip);
+
+		ret = 0;
+	}
+
+	return ret;
+}
+
+
 /* Command option callbacks */
 
 #ifdef HAVE_DWARF_SUPPORT
@@ -299,6 +331,7 @@ static void cleanup_params(void)
 	line_range__clear(&params.line_range);
 	free(params.target);
 	strfilter__delete(params.filter);
+	nsinfo__put(params.nsi);
 	memset(&params, 0, sizeof(params));
 }
 
@@ -554,6 +587,8 @@ __cmd_probe(int argc, const char **argv)
 	OPT_BOOLEAN(0, "cache", &probe_conf.cache, "Manipulate probe cache"),
 	OPT_STRING(0, "symfs", &symbol_conf.symfs, "directory",
 		   "Look for files with symbols relative to this directory"),
+	OPT_CALLBACK(0, "target-ns", NULL, "pid",
+		     "target pid for namespace contexts", opt_set_target_ns),
 	OPT_END()
 	};
 	int ret;
@@ -634,15 +669,15 @@ __cmd_probe(int argc, const char **argv)
 			pr_err_with_code("  Error: Failed to show event list.", ret);
 		return ret;
 	case 'F':
-		ret = show_available_funcs(params.target, params.filter,
-					params.uprobes);
+		ret = show_available_funcs(params.target, params.nsi,
+					   params.filter, params.uprobes);
 		if (ret < 0)
 			pr_err_with_code("  Error: Failed to show functions.", ret);
 		return ret;
 #ifdef HAVE_DWARF_SUPPORT
 	case 'L':
 		ret = show_line_range(&params.line_range, params.target,
-				      params.uprobes);
+				      params.nsi, params.uprobes);
 		if (ret < 0)
 			pr_err_with_code("  Error: Failed to show lines.", ret);
 		return ret;
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index fc5f398..a58e911 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -11,6 +11,7 @@
 #include "event.h"
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <limits.h>
 #include <sched.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -233,3 +234,15 @@ void nsinfo__mountns_exit(struct nscookie *nc)
 		nc->newns = -1;
 	}
 }
+
+char *nsinfo__realpath(const char *path, struct nsinfo *nsi)
+{
+	char *rpath;
+	struct nscookie nsc;
+
+	nsinfo__mountns_enter(nsi, &nsc);
+	rpath = realpath(path, NULL);
+	nsinfo__mountns_exit(&nsc);
+
+	return rpath;
+}
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index f19aa41..05d8260 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -49,6 +49,8 @@ void nsinfo__put(struct nsinfo *nsi);
 void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc);
 void nsinfo__mountns_exit(struct nscookie *nc);
 
+char *nsinfo__realpath(const char *path, struct nsinfo *nsi);
+
 static inline void __nsinfo__zput(struct nsinfo **nsip)
 {
 	if (nsip) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index a2670e9..a80895a 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -184,13 +184,19 @@ static struct map *kernel_get_module_map(const char *module)
 	return NULL;
 }
 
-struct map *get_target_map(const char *target, bool user)
+struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user)
 {
 	/* Init maps of given executable or kernel */
-	if (user)
-		return dso__new_map(target);
-	else
+	if (user) {
+		struct map *map;
+
+		map = dso__new_map(target);
+		if (map && map->dso)
+			map->dso->nsinfo = nsinfo__get(nsi);
+		return map;
+	} else {
 		return kernel_get_module_map(target);
+	}
 }
 
 static int convert_exec_to_group(const char *exec, char **result)
@@ -366,7 +372,8 @@ found:
 static int find_alternative_probe_point(struct debuginfo *dinfo,
 					struct perf_probe_point *pp,
 					struct perf_probe_point *result,
-					const char *target, bool uprobes)
+					const char *target, struct nsinfo *nsi,
+					bool uprobes)
 {
 	struct map *map = NULL;
 	struct symbol *sym;
@@ -377,7 +384,7 @@ static int find_alternative_probe_point(struct debuginfo *dinfo,
 	if (!pp->function || pp->file)
 		return -ENOTSUP;
 
-	map = get_target_map(target, uprobes);
+	map = get_target_map(target, nsi, uprobes);
 	if (!map)
 		return -EINVAL;
 
@@ -421,8 +428,8 @@ static int get_alternative_probe_event(struct debuginfo *dinfo,
 
 	memcpy(tmp, &pev->point, sizeof(*tmp));
 	memset(&pev->point, 0, sizeof(pev->point));
-	ret = find_alternative_probe_point(dinfo, tmp, &pev->point,
-					   pev->target, pev->uprobes);
+	ret = find_alternative_probe_point(dinfo, tmp, &pev->point, pev->target,
+					   pev->nsi, pev->uprobes);
 	if (ret < 0)
 		memcpy(&pev->point, tmp, sizeof(*tmp));
 
@@ -444,7 +451,7 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
 	if (lr->end != INT_MAX)
 		len = lr->end - lr->start;
 	ret = find_alternative_probe_point(dinfo, &pp, &result,
-					   target, user);
+					   target, NULL, user);
 	if (!ret) {
 		lr->function = result.function;
 		lr->file = result.file;
@@ -457,12 +464,14 @@ static int get_alternative_line_range(struct debuginfo *dinfo,
 }
 
 /* Open new debuginfo of given module */
-static struct debuginfo *open_debuginfo(const char *module, bool silent)
+static struct debuginfo *open_debuginfo(const char *module, struct nsinfo *nsi,
+					bool silent)
 {
 	const char *path = module;
 	char reason[STRERR_BUFSIZE];
 	struct debuginfo *ret = NULL;
 	struct dso *dso = NULL;
+	struct nscookie nsc;
 	int err;
 
 	if (!module || !strchr(module, '/')) {
@@ -480,6 +489,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
 		}
 		path = dso->long_name;
 	}
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = debuginfo__new(path);
 	if (!ret && !silent) {
 		pr_warning("The %s file has no debug information.\n", path);
@@ -489,6 +499,7 @@ static struct debuginfo *open_debuginfo(const char *module, bool silent)
 			pr_warning("Rebuild with -g, ");
 		pr_warning("or install an appropriate debuginfo package.\n");
 	}
+	nsinfo__mountns_exit(&nsc);
 	return ret;
 }
 
@@ -516,7 +527,7 @@ static struct debuginfo *debuginfo_cache__open(const char *module, bool silent)
 		goto out;
 	}
 
-	debuginfo_cache = open_debuginfo(module, silent);
+	debuginfo_cache = open_debuginfo(module, NULL, silent);
 	if (!debuginfo_cache)
 		zfree(&debuginfo_cache_path);
 out:
@@ -531,14 +542,18 @@ static void debuginfo_cache__exit(void)
 }
 
 
-static int get_text_start_address(const char *exec, unsigned long *address)
+static int get_text_start_address(const char *exec, unsigned long *address,
+				  struct nsinfo *nsi)
 {
 	Elf *elf;
 	GElf_Ehdr ehdr;
 	GElf_Shdr shdr;
 	int fd, ret = -ENOENT;
+	struct nscookie nsc;
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	fd = open(exec, O_RDONLY);
+	nsinfo__mountns_exit(&nsc);
 	if (fd < 0)
 		return -errno;
 
@@ -582,7 +597,7 @@ static int find_perf_probe_point_from_dwarf(struct probe_trace_point *tp,
 			ret = -EINVAL;
 			goto error;
 		}
-		ret = get_text_start_address(tp->module, &stext);
+		ret = get_text_start_address(tp->module, &stext, NULL);
 		if (ret < 0)
 			goto error;
 		addr += stext;
@@ -659,7 +674,7 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
 
 	/* Prepare a map for offline binary */
 	map = dso__new_map(pathname);
-	if (!map || get_text_start_address(pathname, &stext) < 0) {
+	if (!map || get_text_start_address(pathname, &stext, NULL) < 0) {
 		pr_warning("Failed to get ELF symbols for %s\n", pathname);
 		return -EINVAL;
 	}
@@ -676,7 +691,8 @@ post_process_offline_probe_trace_events(struct probe_trace_event *tevs,
 }
 
 static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
-					  int ntevs, const char *exec)
+					  int ntevs, const char *exec,
+					  struct nsinfo *nsi)
 {
 	int i, ret = 0;
 	unsigned long stext = 0;
@@ -684,7 +700,7 @@ static int add_exec_to_probe_trace_events(struct probe_trace_event *tevs,
 	if (!exec)
 		return 0;
 
-	ret = get_text_start_address(exec, &stext);
+	ret = get_text_start_address(exec, &stext, nsi);
 	if (ret < 0)
 		return ret;
 
@@ -715,7 +731,7 @@ post_process_module_probe_trace_events(struct probe_trace_event *tevs,
 	if (!module)
 		return 0;
 
-	map = get_target_map(module, false);
+	map = get_target_map(module, NULL, false);
 	if (!map || debuginfo__get_text_offset(dinfo, &text_offs, true) < 0) {
 		pr_warning("Failed to get ELF symbols for %s\n", module);
 		return -EINVAL;
@@ -802,7 +818,8 @@ static int post_process_probe_trace_events(struct perf_probe_event *pev,
 	int ret;
 
 	if (uprobe)
-		ret = add_exec_to_probe_trace_events(tevs, ntevs, module);
+		ret = add_exec_to_probe_trace_events(tevs, ntevs, module,
+						     pev->nsi);
 	else if (module)
 		/* Currently ref_reloc_sym based probe is not for drivers */
 		ret = post_process_module_probe_trace_events(tevs, ntevs,
@@ -825,7 +842,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 	struct debuginfo *dinfo;
 	int ntevs, ret = 0;
 
-	dinfo = open_debuginfo(pev->target, !need_dwarf);
+	dinfo = open_debuginfo(pev->target, pev->nsi, !need_dwarf);
 	if (!dinfo) {
 		if (need_dwarf)
 			return -ENOENT;
@@ -945,7 +962,7 @@ static int __show_line_range(struct line_range *lr, const char *module,
 	char sbuf[STRERR_BUFSIZE];
 
 	/* Search a line range */
-	dinfo = open_debuginfo(module, false);
+	dinfo = open_debuginfo(module, NULL, false);
 	if (!dinfo)
 		return -ENOENT;
 
@@ -1021,14 +1038,18 @@ end:
 	return ret;
 }
 
-int show_line_range(struct line_range *lr, const char *module, bool user)
+int show_line_range(struct line_range *lr, const char *module,
+		    struct nsinfo *nsi, bool user)
 {
 	int ret;
+	struct nscookie nsc;
 
 	ret = init_probe_symbol_maps(user);
 	if (ret < 0)
 		return ret;
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = __show_line_range(lr, module, user);
+	nsinfo__mountns_exit(&nsc);
 	exit_probe_symbol_maps();
 
 	return ret;
@@ -1111,7 +1132,7 @@ int show_available_vars(struct perf_probe_event *pevs, int npevs,
 	if (ret < 0)
 		return ret;
 
-	dinfo = open_debuginfo(pevs->target, false);
+	dinfo = open_debuginfo(pevs->target, pevs->nsi, false);
 	if (!dinfo) {
 		ret = -ENOENT;
 		goto out;
@@ -1155,6 +1176,7 @@ static int try_to_find_probe_trace_events(struct perf_probe_event *pev,
 
 int show_line_range(struct line_range *lr __maybe_unused,
 		    const char *module __maybe_unused,
+		    struct nsinfo *nsi __maybe_unused,
 		    bool user __maybe_unused)
 {
 	pr_warning("Debuginfo-analysis is not supported.\n");
@@ -2703,6 +2725,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	struct probe_trace_event *tev = NULL;
 	struct probe_cache *cache = NULL;
 	struct strlist *namelist[2] = {NULL, NULL};
+	struct nscookie nsc;
 
 	up = pev->uprobes ? 1 : 0;
 	fd[up] = __open_probe_file_and_namelist(up, &namelist[up]);
@@ -2729,7 +2752,9 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 		if (ret < 0)
 			break;
 
+		nsinfo__mountns_enter(pev->nsi, &nsc);
 		ret = probe_file__add_event(fd[up], tev);
+		nsinfo__mountns_exit(&nsc);
 		if (ret < 0)
 			break;
 
@@ -2805,7 +2830,7 @@ static int find_probe_trace_events_from_map(struct perf_probe_event *pev,
 	int ret, i, j, skipped = 0;
 	char *mod_name;
 
-	map = get_target_map(pev->target, pev->uprobes);
+	map = get_target_map(pev->target, pev->nsi, pev->uprobes);
 	if (!map) {
 		ret = -EINVAL;
 		goto out;
@@ -3345,13 +3370,16 @@ int apply_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 void cleanup_perf_probe_events(struct perf_probe_event *pevs, int npevs)
 {
 	int i, j;
+	struct perf_probe_event *pev;
 
 	/* Loop 3: cleanup and free trace events  */
 	for (i = 0; i < npevs; i++) {
+		pev = &pevs[i];
 		for (j = 0; j < pevs[i].ntevs; j++)
 			clear_probe_trace_event(&pevs[i].tevs[j]);
 		zfree(&pevs[i].tevs);
 		pevs[i].ntevs = 0;
+		nsinfo__zput(pev->nsi);
 		clear_perf_probe_event(&pevs[i]);
 	}
 }
@@ -3409,8 +3437,8 @@ out:
 	return ret;
 }
 
-int show_available_funcs(const char *target, struct strfilter *_filter,
-					bool user)
+int show_available_funcs(const char *target, struct nsinfo *nsi,
+			 struct strfilter *_filter, bool user)
 {
         struct rb_node *nd;
 	struct map *map;
@@ -3421,7 +3449,7 @@ int show_available_funcs(const char *target, struct strfilter *_filter,
 		return ret;
 
 	/* Get a symbol map */
-	map = get_target_map(target, user);
+	map = get_target_map(target, nsi, user);
 	if (!map) {
 		pr_err("Failed to get a map for %s\n", (target) ? : "kernel");
 		return -EINVAL;
diff --git a/tools/perf/util/probe-event.h b/tools/perf/util/probe-event.h
index 5812947..078681d 100644
--- a/tools/perf/util/probe-event.h
+++ b/tools/perf/util/probe-event.h
@@ -4,6 +4,7 @@
 #include <linux/compiler.h>
 #include <stdbool.h>
 #include "intlist.h"
+#include "namespaces.h"
 
 /* Probe related configurations */
 struct probe_conf {
@@ -92,6 +93,7 @@ struct perf_probe_event {
 	struct perf_probe_arg	*args;	/* Arguments */
 	struct probe_trace_event *tevs;
 	int			ntevs;
+	struct nsinfo		*nsi;	/* Target namespace */
 };
 
 /* Line range */
@@ -163,10 +165,12 @@ int show_perf_probe_event(const char *group, const char *event,
 			  struct perf_probe_event *pev,
 			  const char *module, bool use_stdout);
 int show_perf_probe_events(struct strfilter *filter);
-int show_line_range(struct line_range *lr, const char *module, bool user);
+int show_line_range(struct line_range *lr, const char *module,
+		    struct nsinfo *nsi, bool user);
 int show_available_vars(struct perf_probe_event *pevs, int npevs,
 			struct strfilter *filter);
-int show_available_funcs(const char *module, struct strfilter *filter, bool user);
+int show_available_funcs(const char *module, struct nsinfo *nsi,
+			 struct strfilter *filter, bool user);
 void arch__fix_tev_from_maps(struct perf_probe_event *pev,
 			     struct probe_trace_event *tev, struct map *map,
 			     struct symbol *sym);
@@ -180,7 +184,7 @@ int e_snprintf(char *str, size_t size, const char *format, ...) __printf(3, 4);
 int copy_to_probe_trace_arg(struct probe_trace_arg *tvar,
 			    struct perf_probe_arg *pvar);
 
-struct map *get_target_map(const char *target, bool user);
+struct map *get_target_map(const char *target, struct nsinfo *nsi, bool user);
 
 void arch__post_process_probe_trace_events(struct perf_probe_event *pev,
 					   int ntevs);

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [tip:perf/core] perf buildid-cache: Support binary objects from other namespaces
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 4/6] perf buildid-cache: support binary objects from other namespaces Krister Johansen
@ 2017-07-20  8:49           ` tip-bot for Krister Johansen
  0 siblings, 0 replies; 42+ messages in thread
From: tip-bot for Krister Johansen @ 2017-07-20  8:49 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: peterz, tglx, brendan.d.gregg, linux-kernel, mingo,
	alexander.shishkin, hpa, kjlx, acme, tmricht

Commit-ID:  f045b8c4b36baddcfbdd4d3d956446e688b0b3cd
Gitweb:     http://git.kernel.org/tip/f045b8c4b36baddcfbdd4d3d956446e688b0b3cd
Author:     Krister Johansen <kjlx@templeofstupid.com>
AuthorDate: Wed, 5 Jul 2017 18:48:11 -0700
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Tue, 18 Jul 2017 23:14:11 -0300

perf buildid-cache: Support binary objects from other namespaces

Teach buildid-cache how to add, remove, and update binary objects from
other mount namespaces.  Allow probe events tracing binaries in
different namespaces to add their objects to the probe and build-id
caches too.  As a handy side effect, this also lets us access SDT probes
in binaries from alternate mount namespaces.

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-5-git-send-email-kjlx@templeofstupid.com
[ Add util/namespaces.c to tools/perf/util/python-ext-sources, to fix the python binding 'perf test' ]
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/Documentation/perf-buildid-cache.txt |  5 +++
 tools/perf/Documentation/perf-probe.txt         |  5 +++
 tools/perf/builtin-buildid-cache.c              | 52 ++++++++++++++++++-------
 tools/perf/builtin-probe.c                      |  2 +-
 tools/perf/tests/sdt.c                          |  4 +-
 tools/perf/util/build-id.c                      | 47 ++++++++++++++--------
 tools/perf/util/build-id.h                      |  9 +++--
 tools/perf/util/dso.c                           | 12 +++++-
 tools/perf/util/parse-events.c                  |  2 +-
 tools/perf/util/probe-event.c                   |  6 +--
 tools/perf/util/probe-file.c                    | 19 +++++----
 tools/perf/util/probe-file.h                    |  4 +-
 tools/perf/util/python-ext-sources              |  1 +
 tools/perf/util/symbol.c                        | 19 ++++++---
 tools/perf/util/util.c                          | 34 +++++++++++++---
 tools/perf/util/util.h                          |  2 +
 16 files changed, 160 insertions(+), 63 deletions(-)

diff --git a/tools/perf/Documentation/perf-buildid-cache.txt b/tools/perf/Documentation/perf-buildid-cache.txt
index 058064d..8468100 100644
--- a/tools/perf/Documentation/perf-buildid-cache.txt
+++ b/tools/perf/Documentation/perf-buildid-cache.txt
@@ -61,6 +61,11 @@ OPTIONS
 --verbose::
 	Be more verbose.
 
+--target-ns=PID:
+	Obtain mount namespace information from the target pid.  This is
+	used when creating a uprobe for a process that resides in a
+	different mount namespace from the perf(1) utility.
+
 SEE ALSO
 --------
 linkperf:perf-record[1], linkperf:perf-report[1], linkperf:perf-buildid-list[1]
diff --git a/tools/perf/Documentation/perf-probe.txt b/tools/perf/Documentation/perf-probe.txt
index a42aabc..d7e4869 100644
--- a/tools/perf/Documentation/perf-probe.txt
+++ b/tools/perf/Documentation/perf-probe.txt
@@ -273,6 +273,11 @@ Add a uprobe to a target process running in a different mount namespace
 
  ./perf probe --target-ns <target pid> -x /lib64/libc.so.6 malloc
 
+Add a USDT probe to a target process running in a different mount namespace
+
+ ./perf probe --target-ns <target pid> -x /usr/lib/jvm/java-1.8.0-openjdk-1.8.0.121-0.b13.el7_3.x86_64/jre/lib/amd64/server/libjvm.so %sdt_hotspot:thread__sleep__end
+
+
 SEE ALSO
 --------
 linkperf:perf-trace[1], linkperf:perf-record[1], linkperf:perf-buildid-cache[1]
diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index 9eba7f1..d65bd86 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -14,6 +14,7 @@
 #include <unistd.h>
 #include "builtin.h"
 #include "perf.h"
+#include "namespaces.h"
 #include "util/cache.h"
 #include "util/debug.h"
 #include "util/header.h"
@@ -165,33 +166,41 @@ static int build_id_cache__add_kcore(const char *filename, bool force)
 	return 0;
 }
 
-static int build_id_cache__add_file(const char *filename)
+static int build_id_cache__add_file(const char *filename, struct nsinfo *nsi)
 {
 	char sbuild_id[SBUILD_ID_SIZE];
 	u8 build_id[BUILD_ID_SIZE];
 	int err;
+	struct nscookie nsc;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
-	err = build_id_cache__add_s(sbuild_id, filename,
+	err = build_id_cache__add_s(sbuild_id, filename, nsi,
 				    false, false);
 	pr_debug("Adding %s %s: %s\n", sbuild_id, filename,
 		 err ? "FAIL" : "Ok");
 	return err;
 }
 
-static int build_id_cache__remove_file(const char *filename)
+static int build_id_cache__remove_file(const char *filename, struct nsinfo *nsi)
 {
 	u8 build_id[BUILD_ID_SIZE];
 	char sbuild_id[SBUILD_ID_SIZE];
+	struct nscookie nsc;
 
 	int err;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
@@ -204,13 +213,13 @@ static int build_id_cache__remove_file(const char *filename)
 	return err;
 }
 
-static int build_id_cache__purge_path(const char *pathname)
+static int build_id_cache__purge_path(const char *pathname, struct nsinfo *nsi)
 {
 	struct strlist *list;
 	struct str_node *pos;
 	int err;
 
-	err = build_id_cache__list_build_ids(pathname, &list);
+	err = build_id_cache__list_build_ids(pathname, nsi, &list);
 	if (err)
 		goto out;
 
@@ -256,24 +265,30 @@ static int build_id_cache__fprintf_missing(struct perf_session *session, FILE *f
 	return 0;
 }
 
-static int build_id_cache__update_file(const char *filename)
+static int build_id_cache__update_file(const char *filename, struct nsinfo *nsi)
 {
 	u8 build_id[BUILD_ID_SIZE];
 	char sbuild_id[SBUILD_ID_SIZE];
+	struct nscookie nsc;
 
-	int err = 0;
+	int err;
 
-	if (filename__read_build_id(filename, &build_id, sizeof(build_id)) < 0) {
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = filename__read_build_id(filename, &build_id, sizeof(build_id));
+	nsinfo__mountns_exit(&nsc);
+	if (err < 0) {
 		pr_debug("Couldn't read a build-id in %s\n", filename);
 		return -1;
 	}
+	err = 0;
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
 	if (build_id_cache__cached(sbuild_id))
 		err = build_id_cache__remove_s(sbuild_id);
 
 	if (!err)
-		err = build_id_cache__add_s(sbuild_id, filename, false, false);
+		err = build_id_cache__add_s(sbuild_id, filename, nsi, false,
+					    false);
 
 	pr_debug("Updating %s %s: %s\n", sbuild_id, filename,
 		 err ? "FAIL" : "Ok");
@@ -286,6 +301,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 	struct strlist *list;
 	struct str_node *pos;
 	int ret = 0;
+	int ns_id = -1;
 	bool force = false;
 	char const *add_name_list_str = NULL,
 		   *remove_name_list_str = NULL,
@@ -299,6 +315,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		.mode  = PERF_DATA_MODE_READ,
 	};
 	struct perf_session *session = NULL;
+	struct nsinfo *nsi = NULL;
 
 	const struct option buildid_cache_options[] = {
 	OPT_STRING('a', "add", &add_name_list_str,
@@ -315,6 +332,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 	OPT_STRING('u', "update", &update_name_list_str, "file list",
 		    "file(s) to update"),
 	OPT_INCR('v', "verbose", &verbose, "be more verbose"),
+	OPT_INTEGER(0, "target-ns", &ns_id, "target pid for namespace context"),
 	OPT_END()
 	};
 	const char * const buildid_cache_usage[] = {
@@ -330,6 +348,9 @@ int cmd_buildid_cache(int argc, const char **argv)
 		     !missing_filename && !update_name_list_str))
 		usage_with_options(buildid_cache_usage, buildid_cache_options);
 
+	if (ns_id > 0)
+		nsi = nsinfo__new(ns_id);
+
 	if (missing_filename) {
 		file.path = missing_filename;
 		file.force = force;
@@ -348,7 +369,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(add_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__add_file(pos->s)) {
+				if (build_id_cache__add_file(pos->s, nsi)) {
 					if (errno == EEXIST) {
 						pr_debug("%s already in the cache\n",
 							 pos->s);
@@ -366,7 +387,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(remove_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__remove_file(pos->s)) {
+				if (build_id_cache__remove_file(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -384,7 +405,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(purge_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__purge_path(pos->s)) {
+				if (build_id_cache__purge_path(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -405,7 +426,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 		list = strlist__new(update_name_list_str, NULL);
 		if (list) {
 			strlist__for_each_entry(pos, list)
-				if (build_id_cache__update_file(pos->s)) {
+				if (build_id_cache__update_file(pos->s, nsi)) {
 					if (errno == ENOENT) {
 						pr_debug("%s wasn't in the cache\n",
 							 pos->s);
@@ -424,6 +445,7 @@ int cmd_buildid_cache(int argc, const char **argv)
 
 out:
 	perf_session__delete(session);
+	nsinfo__zput(nsi);
 
 	return ret;
 }
diff --git a/tools/perf/builtin-probe.c b/tools/perf/builtin-probe.c
index 3fb98d5..c006592 100644
--- a/tools/perf/builtin-probe.c
+++ b/tools/perf/builtin-probe.c
@@ -416,7 +416,7 @@ static int del_perf_probe_caches(struct strfilter *filter)
 	}
 
 	strlist__for_each_entry(nd, bidlist) {
-		cache = probe_cache__new(nd->s);
+		cache = probe_cache__new(nd->s, NULL);
 		if (!cache)
 			continue;
 		if (probe_cache__filter_purge(cache, filter) < 0 ||
diff --git a/tools/perf/tests/sdt.c b/tools/perf/tests/sdt.c
index 06eda67..5abe8ce 100644
--- a/tools/perf/tests/sdt.c
+++ b/tools/perf/tests/sdt.c
@@ -33,7 +33,7 @@ static int build_id_cache__add_file(const char *filename)
 	}
 
 	build_id__sprintf(build_id, sizeof(build_id), sbuild_id);
-	err = build_id_cache__add_s(sbuild_id, filename, false, false);
+	err = build_id_cache__add_s(sbuild_id, filename, NULL, false, false);
 	if (err < 0)
 		pr_debug("Failed to add build id cache of %s\n", filename);
 	return err;
@@ -54,7 +54,7 @@ static char *get_self_path(void)
 static int search_cached_probe(const char *target,
 			       const char *group, const char *event)
 {
-	struct probe_cache *cache = probe_cache__new(target);
+	struct probe_cache *cache = probe_cache__new(target, NULL);
 	int ret = 0;
 
 	if (!cache) {
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index e0148b0..f7bfd90 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -534,13 +534,14 @@ char *build_id_cache__complement(const char *incomplete_sbuild_id)
 }
 
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
-			       bool is_kallsyms, bool is_vdso)
+			       struct nsinfo *nsi, bool is_kallsyms,
+			       bool is_vdso)
 {
 	char *realname = (char *)name, *filename;
 	bool slash = is_kallsyms || is_vdso;
 
 	if (!slash) {
-		realname = realpath(name, NULL);
+		realname = nsinfo__realpath(name, nsi);
 		if (!realname)
 			return NULL;
 	}
@@ -556,13 +557,13 @@ char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
 	return filename;
 }
 
-int build_id_cache__list_build_ids(const char *pathname,
+int build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi,
 				   struct strlist **result)
 {
 	char *dir_name;
 	int ret = 0;
 
-	dir_name = build_id_cache__cachedir(NULL, pathname, false, false);
+	dir_name = build_id_cache__cachedir(NULL, pathname, nsi, false, false);
 	if (!dir_name)
 		return -ENOMEM;
 
@@ -576,16 +577,20 @@ int build_id_cache__list_build_ids(const char *pathname,
 
 #if defined(HAVE_LIBELF_SUPPORT) && defined(HAVE_GELF_GETNOTE_SUPPORT)
 static int build_id_cache__add_sdt_cache(const char *sbuild_id,
-					  const char *realname)
+					  const char *realname,
+					  struct nsinfo *nsi)
 {
 	struct probe_cache *cache;
 	int ret;
+	struct nscookie nsc;
 
-	cache = probe_cache__new(sbuild_id);
+	cache = probe_cache__new(sbuild_id, nsi);
 	if (!cache)
 		return -1;
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	ret = probe_cache__scan_sdt(cache, realname);
+	nsinfo__mountns_exit(&nsc);
 	if (ret >= 0) {
 		pr_debug4("Found %d SDTs in %s\n", ret, realname);
 		if (probe_cache__commit(cache) < 0)
@@ -595,11 +600,11 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id,
 	return ret;
 }
 #else
-#define build_id_cache__add_sdt_cache(sbuild_id, realname) (0)
+#define build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) (0)
 #endif
 
 int build_id_cache__add_s(const char *sbuild_id, const char *name,
-			  bool is_kallsyms, bool is_vdso)
+			  struct nsinfo *nsi, bool is_kallsyms, bool is_vdso)
 {
 	const size_t size = PATH_MAX;
 	char *realname = NULL, *filename = NULL, *dir_name = NULL,
@@ -607,13 +612,16 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 	int err = -1;
 
 	if (!is_kallsyms) {
-		realname = realpath(name, NULL);
+		if (!is_vdso)
+			realname = nsinfo__realpath(name, nsi);
+		else
+			realname = realpath(name, NULL);
 		if (!realname)
 			goto out_free;
 	}
 
-	dir_name = build_id_cache__cachedir(sbuild_id, name,
-					    is_kallsyms, is_vdso);
+	dir_name = build_id_cache__cachedir(sbuild_id, name, nsi, is_kallsyms,
+					    is_vdso);
 	if (!dir_name)
 		goto out_free;
 
@@ -634,7 +642,10 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 
 	if (access(filename, F_OK)) {
 		if (is_kallsyms) {
-			 if (copyfile("/proc/kallsyms", filename))
+			if (copyfile("/proc/kallsyms", filename))
+				goto out_free;
+		} else if (nsi && nsi->need_setns) {
+			if (copyfile_ns(name, filename, nsi))
 				goto out_free;
 		} else if (link(realname, filename) && errno != EEXIST &&
 				copyfile(name, filename))
@@ -657,7 +668,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 		err = 0;
 
 	/* Update SDT cache : error is just warned */
-	if (realname && build_id_cache__add_sdt_cache(sbuild_id, realname) < 0)
+	if (realname &&
+	    build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) < 0)
 		pr_debug4("Failed to update/scan SDT cache for %s\n", realname);
 
 out_free:
@@ -670,14 +682,15 @@ out_free:
 }
 
 static int build_id_cache__add_b(const u8 *build_id, size_t build_id_size,
-				 const char *name, bool is_kallsyms,
-				 bool is_vdso)
+				 const char *name, struct nsinfo *nsi,
+				 bool is_kallsyms, bool is_vdso)
 {
 	char sbuild_id[SBUILD_ID_SIZE];
 
 	build_id__sprintf(build_id, build_id_size, sbuild_id);
 
-	return build_id_cache__add_s(sbuild_id, name, is_kallsyms, is_vdso);
+	return build_id_cache__add_s(sbuild_id, name, nsi, is_kallsyms,
+				     is_vdso);
 }
 
 bool build_id_cache__cached(const char *sbuild_id)
@@ -743,7 +756,7 @@ static int dso__cache_build_id(struct dso *dso, struct machine *machine)
 		name = nm;
 	}
 	return build_id_cache__add_b(dso->build_id, sizeof(dso->build_id), name,
-				     is_kallsyms, is_vdso);
+				     dso->nsinfo, is_kallsyms, is_vdso);
 }
 
 static int __dsos__cache_build_ids(struct list_head *head,
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 96690a5..2397084 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -5,6 +5,7 @@
 #define SBUILD_ID_SIZE	(BUILD_ID_SIZE * 2 + 1)
 
 #include "tool.h"
+#include "namespaces.h"
 #include <linux/types.h>
 
 extern struct perf_tool build_id__mark_dso_hit_ops;
@@ -31,17 +32,19 @@ int perf_session__cache_build_ids(struct perf_session *session);
 char *build_id_cache__origname(const char *sbuild_id);
 char *build_id_cache__linkname(const char *sbuild_id, char *bf, size_t size);
 char *build_id_cache__cachedir(const char *sbuild_id, const char *name,
-			       bool is_kallsyms, bool is_vdso);
+			       struct nsinfo *nsi, bool is_kallsyms,
+			       bool is_vdso);
 
 struct strlist;
 
 struct strlist *build_id_cache__list_all(bool validonly);
 char *build_id_cache__complement(const char *incomplete_sbuild_id);
-int build_id_cache__list_build_ids(const char *pathname,
+int build_id_cache__list_build_ids(const char *pathname, struct nsinfo *nsi,
 				   struct strlist **result);
 bool build_id_cache__cached(const char *sbuild_id);
 int build_id_cache__add_s(const char *sbuild_id,
-			  const char *name, bool is_kallsyms, bool is_vdso);
+			  const char *name, struct nsinfo *nsi,
+			  bool is_kallsyms, bool is_vdso);
 int build_id_cache__remove_s(const char *sbuild_id);
 
 extern char buildid_dir[];
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index beda40e..dc9b495 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -504,7 +504,14 @@ static void check_data_close(void);
  */
 static int open_dso(struct dso *dso, struct machine *machine)
 {
-	int fd = __open_dso(dso, machine);
+	int fd;
+	struct nscookie nsc;
+
+	if (dso->binary_type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		nsinfo__mountns_enter(dso->nsinfo, &nsc);
+	fd = __open_dso(dso, machine);
+	if (dso->binary_type != DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		nsinfo__mountns_exit(&nsc);
 
 	if (fd >= 0) {
 		dso__list_add(dso);
@@ -1302,6 +1309,7 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
 {
 	bool have_build_id = false;
 	struct dso *pos;
+	struct nscookie nsc;
 
 	list_for_each_entry(pos, head, node) {
 		if (with_hits && !pos->hit && !dso__is_vdso(pos))
@@ -1310,11 +1318,13 @@ bool __dsos__read_build_ids(struct list_head *head, bool with_hits)
 			have_build_id = true;
 			continue;
 		}
+		nsinfo__mountns_enter(pos->nsinfo, &nsc);
 		if (filename__read_build_id(pos->long_name, pos->build_id,
 					    sizeof(pos->build_id)) > 0) {
 			have_build_id	  = true;
 			pos->has_build_id = true;
 		}
+		nsinfo__mountns_exit(&nsc);
 	}
 
 	return have_build_id;
diff --git a/tools/perf/util/parse-events.c b/tools/perf/util/parse-events.c
index 01e779b..84e3010 100644
--- a/tools/perf/util/parse-events.c
+++ b/tools/perf/util/parse-events.c
@@ -2124,7 +2124,7 @@ void print_sdt_events(const char *subsys_glob, const char *event_glob,
 		return;
 	}
 	strlist__for_each_entry(nd, bidlist) {
-		pcache = probe_cache__new(nd->s);
+		pcache = probe_cache__new(nd->s, NULL);
 		if (!pcache)
 			continue;
 		list_for_each_entry(ent, &pcache->entries, node) {
diff --git a/tools/perf/util/probe-event.c b/tools/perf/util/probe-event.c
index a80895a..d7cd114 100644
--- a/tools/perf/util/probe-event.c
+++ b/tools/perf/util/probe-event.c
@@ -2769,7 +2769,7 @@ static int __add_probe_trace_events(struct perf_probe_event *pev,
 	if (ret == -EINVAL && pev->uprobes)
 		warn_uprobe_event_compat(tev);
 	if (ret == 0 && probe_conf.cache) {
-		cache = probe_cache__new(pev->target);
+		cache = probe_cache__new(pev->target, pev->nsi);
 		if (!cache ||
 		    probe_cache__add_entry(cache, pev, tevs, ntevs) < 0 ||
 		    probe_cache__commit(cache) < 0)
@@ -3119,7 +3119,7 @@ static int find_cached_events(struct perf_probe_event *pev,
 	int ntevs = 0;
 	int ret = 0;
 
-	cache = probe_cache__new(target);
+	cache = probe_cache__new(target, pev->nsi);
 	/* Return 0 ("not found") if the target has no probe cache. */
 	if (!cache)
 		return 0;
@@ -3209,7 +3209,7 @@ static int find_probe_trace_events_from_cache(struct perf_probe_event *pev,
 		else
 			return find_cached_events(pev, tevs, pev->target);
 	}
-	cache = probe_cache__new(pev->target);
+	cache = probe_cache__new(pev->target, pev->nsi);
 	if (!cache)
 		return 0;
 
diff --git a/tools/perf/util/probe-file.c b/tools/perf/util/probe-file.c
index d679389..cdf8d83 100644
--- a/tools/perf/util/probe-file.c
+++ b/tools/perf/util/probe-file.c
@@ -412,13 +412,15 @@ int probe_cache_entry__get_event(struct probe_cache_entry *entry,
 }
 
 /* For the kernel probe caches, pass target = NULL or DSO__NAME_KALLSYMS */
-static int probe_cache__open(struct probe_cache *pcache, const char *target)
+static int probe_cache__open(struct probe_cache *pcache, const char *target,
+			     struct nsinfo *nsi)
 {
 	char cpath[PATH_MAX];
 	char sbuildid[SBUILD_ID_SIZE];
 	char *dir_name = NULL;
 	bool is_kallsyms = false;
 	int ret, fd;
+	struct nscookie nsc;
 
 	if (target && build_id_cache__cached(target)) {
 		/* This is a cached buildid */
@@ -431,8 +433,11 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 		target = DSO__NAME_KALLSYMS;
 		is_kallsyms = true;
 		ret = sysfs__sprintf_build_id("/", sbuildid);
-	} else
+	} else {
+		nsinfo__mountns_enter(nsi, &nsc);
 		ret = filename__sprintf_build_id(target, sbuildid);
+		nsinfo__mountns_exit(&nsc);
+	}
 
 	if (ret < 0) {
 		pr_debug("Failed to get build-id from %s.\n", target);
@@ -441,7 +446,7 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 
 	/* If we have no buildid cache, make it */
 	if (!build_id_cache__cached(sbuildid)) {
-		ret = build_id_cache__add_s(sbuildid, target,
+		ret = build_id_cache__add_s(sbuildid, target, nsi,
 					    is_kallsyms, NULL);
 		if (ret < 0) {
 			pr_debug("Failed to add build-id cache: %s\n", target);
@@ -449,7 +454,7 @@ static int probe_cache__open(struct probe_cache *pcache, const char *target)
 		}
 	}
 
-	dir_name = build_id_cache__cachedir(sbuildid, target, is_kallsyms,
+	dir_name = build_id_cache__cachedir(sbuildid, target, nsi, is_kallsyms,
 					    false);
 found:
 	if (!dir_name) {
@@ -554,7 +559,7 @@ void probe_cache__delete(struct probe_cache *pcache)
 	free(pcache);
 }
 
-struct probe_cache *probe_cache__new(const char *target)
+struct probe_cache *probe_cache__new(const char *target, struct nsinfo *nsi)
 {
 	struct probe_cache *pcache = probe_cache__alloc();
 	int ret;
@@ -562,7 +567,7 @@ struct probe_cache *probe_cache__new(const char *target)
 	if (!pcache)
 		return NULL;
 
-	ret = probe_cache__open(pcache, target);
+	ret = probe_cache__open(pcache, target, nsi);
 	if (ret < 0) {
 		pr_debug("Cache open error: %d\n", ret);
 		goto out_err;
@@ -974,7 +979,7 @@ int probe_cache__show_all_caches(struct strfilter *filter)
 		return -EINVAL;
 	}
 	strlist__for_each_entry(nd, bidlist) {
-		pcache = probe_cache__new(nd->s);
+		pcache = probe_cache__new(nd->s, NULL);
 		if (!pcache)
 			continue;
 		if (!list_empty(&pcache->entries)) {
diff --git a/tools/perf/util/probe-file.h b/tools/perf/util/probe-file.h
index 5ecc9d3..2ca4163 100644
--- a/tools/perf/util/probe-file.h
+++ b/tools/perf/util/probe-file.h
@@ -51,7 +51,7 @@ int probe_file__del_strlist(int fd, struct strlist *namelist);
 int probe_cache_entry__get_event(struct probe_cache_entry *entry,
 				 struct probe_trace_event **tevs);
 
-struct probe_cache *probe_cache__new(const char *target);
+struct probe_cache *probe_cache__new(const char *target, struct nsinfo *nsi);
 int probe_cache__add_entry(struct probe_cache *pcache,
 			   struct perf_probe_event *pev,
 			   struct probe_trace_event *tevs, int ntevs);
@@ -69,7 +69,7 @@ int probe_cache__show_all_caches(struct strfilter *filter);
 bool probe_type_is_available(enum probe_type type);
 bool kretprobe_offset_is_supported(void);
 #else	/* ! HAVE_LIBELF_SUPPORT */
-static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused)
+static inline struct probe_cache *probe_cache__new(const char *tgt __maybe_unused, struct nsinfo *nsi __maybe_unused)
 {
 	return NULL;
 }
diff --git a/tools/perf/util/python-ext-sources b/tools/perf/util/python-ext-sources
index 9f3b0d9..e66dc49 100644
--- a/tools/perf/util/python-ext-sources
+++ b/tools/perf/util/python-ext-sources
@@ -10,6 +10,7 @@ util/ctype.c
 util/evlist.c
 util/evsel.c
 util/cpumap.c
+util/namespaces.c
 ../lib/bitmap.c
 ../lib/find_bit.c
 ../lib/hweight.c
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 21c97cc..8c7bae5 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -1464,7 +1464,6 @@ static int dso__find_perf_map(char *filebuf, size_t bufsz,
 	return rc;
 }
 
-
 int dso__load(struct dso *dso, struct map *map)
 {
 	char *name;
@@ -1565,6 +1564,8 @@ int dso__load(struct dso *dso, struct map *map)
 	for (i = 0; i < DSO_BINARY_TYPE__SYMTAB_CNT; i++) {
 		struct symsrc *ss = &ss_[ss_pos];
 		bool next_slot = false;
+		bool is_reg;
+		int sirc;
 
 		enum dso_binary_type symtab_type = binary_type_symtab[i];
 
@@ -1575,12 +1576,20 @@ int dso__load(struct dso *dso, struct map *map)
 						   root_dir, name, PATH_MAX))
 			continue;
 
-		if (!is_regular_file(name))
-			continue;
+		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+			nsinfo__mountns_exit(&nsc);
+
+		is_reg = is_regular_file(name);
+		sirc = symsrc__init(ss, dso, name, symtab_type);
 
-		/* Name is now the name of the next image to try */
-		if (symsrc__init(ss, dso, name, symtab_type) < 0)
+		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+			nsinfo__mountns_enter(dso->nsinfo, &nsc);
+
+		if (!is_reg || sirc < 0) {
+			if (sirc >= 0)
+				symsrc__destroy(ss);
 			continue;
+		}
 
 		if (!syms_ss && symsrc__has_symtab(ss)) {
 			syms_ss = ss;
diff --git a/tools/perf/util/util.c b/tools/perf/util/util.c
index 988111e..9e4ea85 100644
--- a/tools/perf/util/util.c
+++ b/tools/perf/util/util.c
@@ -143,13 +143,17 @@ out:
 	return list;
 }
 
-static int slow_copyfile(const char *from, const char *to)
+static int slow_copyfile(const char *from, const char *to, struct nsinfo *nsi)
 {
 	int err = -1;
 	char *line = NULL;
 	size_t n;
-	FILE *from_fp = fopen(from, "r"), *to_fp;
+	FILE *from_fp, *to_fp;
+	struct nscookie nsc;
 
+	nsinfo__mountns_enter(nsi, &nsc);
+	from_fp = fopen(from, "r");
+	nsinfo__mountns_exit(&nsc);
 	if (from_fp == NULL)
 		goto out;
 
@@ -198,15 +202,21 @@ int copyfile_offset(int ifd, loff_t off_in, int ofd, loff_t off_out, u64 size)
 	return size ? -1 : 0;
 }
 
-int copyfile_mode(const char *from, const char *to, mode_t mode)
+static int copyfile_mode_ns(const char *from, const char *to, mode_t mode,
+			    struct nsinfo *nsi)
 {
 	int fromfd, tofd;
 	struct stat st;
-	int err = -1;
+	int err;
 	char *tmp = NULL, *ptr = NULL;
+	struct nscookie nsc;
 
-	if (stat(from, &st))
+	nsinfo__mountns_enter(nsi, &nsc);
+	err = stat(from, &st);
+	nsinfo__mountns_exit(&nsc);
+	if (err)
 		goto out;
+	err = -1;
 
 	/* extra 'x' at the end is to reserve space for '.' */
 	if (asprintf(&tmp, "%s.XXXXXXx", to) < 0) {
@@ -227,11 +237,13 @@ int copyfile_mode(const char *from, const char *to, mode_t mode)
 		goto out_close_to;
 
 	if (st.st_size == 0) { /* /proc? do it slowly... */
-		err = slow_copyfile(from, tmp);
+		err = slow_copyfile(from, tmp, nsi);
 		goto out_close_to;
 	}
 
+	nsinfo__mountns_enter(nsi, &nsc);
 	fromfd = open(from, O_RDONLY);
+	nsinfo__mountns_exit(&nsc);
 	if (fromfd < 0)
 		goto out_close_to;
 
@@ -248,6 +260,16 @@ out:
 	return err;
 }
 
+int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi)
+{
+	return copyfile_mode_ns(from, to, 0755, nsi);
+}
+
+int copyfile_mode(const char *from, const char *to, mode_t mode)
+{
+	return copyfile_mode_ns(from, to, mode, NULL);
+}
+
 int copyfile(const char *from, const char *to)
 {
 	return copyfile_mode(from, to, 0755);
diff --git a/tools/perf/util/util.h b/tools/perf/util/util.h
index 1e5fe1d..062dd20 100644
--- a/tools/perf/util/util.h
+++ b/tools/perf/util/util.h
@@ -12,6 +12,7 @@
 #include <stdarg.h>
 #include <linux/compiler.h>
 #include <linux/types.h>
+#include "namespaces.h"
 
 /* General helper functions */
 void usage(const char *err) __noreturn;
@@ -33,6 +34,7 @@ struct strlist *lsdir(const char *name, bool (*filter)(const char *, struct dire
 bool lsdir_no_dot_filter(const char *name, struct dirent *d);
 int copyfile(const char *from, const char *to);
 int copyfile_mode(const char *from, const char *to, mode_t mode);
+int copyfile_ns(const char *from, const char *to, struct nsinfo *nsi);
 int copyfile_offset(int fromfd, loff_t from_ofs, int tofd, loff_t to_ofs, u64 size);
 
 ssize_t readn(int fd, void *buf, size_t n);

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* [tip:perf/core] perf buildid-cache: Cache debuginfo
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 6/6] perf buildid-cache: cache debuginfo Krister Johansen
@ 2017-07-20  8:49           ` tip-bot for Krister Johansen
  0 siblings, 0 replies; 42+ messages in thread
From: tip-bot for Krister Johansen @ 2017-07-20  8:49 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: peterz, mingo, brendan.d.gregg, acme, tglx, alexander.shishkin,
	hpa, linux-kernel, kjlx, tmricht

Commit-ID:  d2396999c998b4e0006aef247e154eff0ed3d8f9
Gitweb:     http://git.kernel.org/tip/d2396999c998b4e0006aef247e154eff0ed3d8f9
Author:     Krister Johansen <kjlx@templeofstupid.com>
AuthorDate: Wed, 5 Jul 2017 18:48:13 -0700
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Tue, 18 Jul 2017 23:14:11 -0300

perf buildid-cache: Cache debuginfo

If a stripped binary is placed in the cache, the user is in a situation
where there's a cached elf file present, but it doesn't have any symtab
to use for name resolution.  Grab the debuginfo for binaries that don't
end in .ko.  This yields a better chance of resolving symbols from older
traces.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas-Mich Richter <tmricht@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1499305693-1599-7-git-send-email-kjlx@templeofstupid.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/builtin-buildid-cache.c |  2 +-
 tools/perf/util/annotate.c         |  2 +-
 tools/perf/util/build-id.c         | 72 +++++++++++++++++++++++++++++++++++---
 tools/perf/util/build-id.h         |  3 +-
 tools/perf/util/dso.c              |  8 ++++-
 tools/perf/util/dso.h              |  1 +
 tools/perf/util/machine.c          |  3 +-
 tools/perf/util/symbol.c           | 12 +++++--
 8 files changed, 90 insertions(+), 13 deletions(-)

diff --git a/tools/perf/builtin-buildid-cache.c b/tools/perf/builtin-buildid-cache.c
index d65bd86..e3eb624 100644
--- a/tools/perf/builtin-buildid-cache.c
+++ b/tools/perf/builtin-buildid-cache.c
@@ -243,7 +243,7 @@ static bool dso__missing_buildid_cache(struct dso *dso, int parm __maybe_unused)
 	char filename[PATH_MAX];
 	u8 build_id[BUILD_ID_SIZE];
 
-	if (dso__build_id_filename(dso, filename, sizeof(filename)) &&
+	if (dso__build_id_filename(dso, filename, sizeof(filename), false) &&
 	    filename__read_build_id(filename, build_id,
 				    sizeof(build_id)) != sizeof(build_id)) {
 		if (errno == ENOENT)
diff --git a/tools/perf/util/annotate.c b/tools/perf/util/annotate.c
index ef434b5..1742510 100644
--- a/tools/perf/util/annotate.c
+++ b/tools/perf/util/annotate.c
@@ -1347,7 +1347,7 @@ static int dso__disassemble_filename(struct dso *dso, char *filename, size_t fil
 	    !dso__is_kcore(dso))
 		return SYMBOL_ANNOTATE_ERRNO__NO_VMLINUX;
 
-	build_id_filename = dso__build_id_filename(dso, NULL, 0);
+	build_id_filename = dso__build_id_filename(dso, NULL, 0, false);
 	if (build_id_filename) {
 		__symbol__join_symfs(filename, filename_size, build_id_filename);
 		free(build_id_filename);
diff --git a/tools/perf/util/build-id.c b/tools/perf/util/build-id.c
index f7bfd90..e966515 100644
--- a/tools/perf/util/build-id.c
+++ b/tools/perf/util/build-id.c
@@ -243,12 +243,15 @@ static bool build_id_cache__valid_id(char *sbuild_id)
 	return result;
 }
 
-static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso)
+static const char *build_id_cache__basename(bool is_kallsyms, bool is_vdso,
+					    bool is_debug)
 {
-	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : "elf");
+	return is_kallsyms ? "kallsyms" : (is_vdso ? "vdso" : (is_debug ?
+	    "debug" : "elf"));
 }
 
-char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
+char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size,
+			     bool is_debug)
 {
 	bool is_kallsyms = dso__is_kallsyms((struct dso *)dso);
 	bool is_vdso = dso__is_vdso((struct dso *)dso);
@@ -270,7 +273,8 @@ char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size)
 		ret = asnprintf(&bf, size, "%s", linkname);
 	else
 		ret = asnprintf(&bf, size, "%s/%s", linkname,
-			 build_id_cache__basename(is_kallsyms, is_vdso));
+			 build_id_cache__basename(is_kallsyms, is_vdso,
+						  is_debug));
 	if (ret < 0 || (!alloc && size < (unsigned int)ret))
 		bf = NULL;
 	free(linkname);
@@ -603,12 +607,40 @@ static int build_id_cache__add_sdt_cache(const char *sbuild_id,
 #define build_id_cache__add_sdt_cache(sbuild_id, realname, nsi) (0)
 #endif
 
+static char *build_id_cache__find_debug(const char *sbuild_id,
+					struct nsinfo *nsi)
+{
+	char *realname = NULL;
+	char *debugfile;
+	struct nscookie nsc;
+	size_t len = 0;
+
+	debugfile = calloc(1, PATH_MAX);
+	if (!debugfile)
+		goto out;
+
+	len = __symbol__join_symfs(debugfile, PATH_MAX,
+				   "/usr/lib/debug/.build-id/");
+	snprintf(debugfile + len, PATH_MAX - len, "%.2s/%s.debug", sbuild_id,
+		 sbuild_id + 2);
+
+	nsinfo__mountns_enter(nsi, &nsc);
+	realname = realpath(debugfile, NULL);
+	if (realname && access(realname, R_OK))
+		zfree(&realname);
+	nsinfo__mountns_exit(&nsc);
+out:
+	free(debugfile);
+	return realname;
+}
+
 int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			  struct nsinfo *nsi, bool is_kallsyms, bool is_vdso)
 {
 	const size_t size = PATH_MAX;
 	char *realname = NULL, *filename = NULL, *dir_name = NULL,
 	     *linkname = zalloc(size), *tmp;
+	char *debugfile = NULL;
 	int err = -1;
 
 	if (!is_kallsyms) {
@@ -635,7 +667,8 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 
 	/* Save the allocated buildid dirname */
 	if (asprintf(&filename, "%s/%s", dir_name,
-		     build_id_cache__basename(is_kallsyms, is_vdso)) < 0) {
+		     build_id_cache__basename(is_kallsyms, is_vdso,
+		     false)) < 0) {
 		filename = NULL;
 		goto out_free;
 	}
@@ -652,6 +685,34 @@ int build_id_cache__add_s(const char *sbuild_id, const char *name,
 			goto out_free;
 	}
 
+	/* Some binaries are stripped, but have .debug files with their symbol
+	 * table.  Check to see if we can locate one of those, since the elf
+	 * file itself may not be very useful to users of our tools without a
+	 * symtab.
+	 */
+	if (!is_kallsyms && !is_vdso &&
+	    strncmp(".ko", name + strlen(name) - 3, 3)) {
+		debugfile = build_id_cache__find_debug(sbuild_id, nsi);
+		if (debugfile) {
+			zfree(&filename);
+			if (asprintf(&filename, "%s/%s", dir_name,
+			    build_id_cache__basename(false, false, true)) < 0) {
+				filename = NULL;
+				goto out_free;
+			}
+			if (access(filename, F_OK)) {
+				if (nsi && nsi->need_setns) {
+					if (copyfile_ns(debugfile, filename,
+							nsi))
+						goto out_free;
+				} else if (link(debugfile, filename) &&
+						errno != EEXIST &&
+						copyfile(debugfile, filename))
+					goto out_free;
+			}
+		}
+	}
+
 	if (!build_id_cache__linkname(sbuild_id, linkname, size))
 		goto out_free;
 	tmp = strrchr(linkname, '/');
@@ -676,6 +737,7 @@ out_free:
 	if (!is_kallsyms)
 		free(realname);
 	free(filename);
+	free(debugfile);
 	free(dir_name);
 	free(linkname);
 	return err;
diff --git a/tools/perf/util/build-id.h b/tools/perf/util/build-id.h
index 2397084..113dc06 100644
--- a/tools/perf/util/build-id.h
+++ b/tools/perf/util/build-id.h
@@ -17,7 +17,8 @@ int filename__sprintf_build_id(const char *pathname, char *sbuild_id);
 char *build_id_cache__kallsyms_path(const char *sbuild_id, char *bf,
 				    size_t size);
 
-char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size);
+char *dso__build_id_filename(const struct dso *dso, char *bf, size_t size,
+			     bool is_debug);
 
 int build_id__mark_dso_hit(struct perf_tool *tool, union perf_event *event,
 			   struct perf_sample *sample, struct perf_evsel *evsel,
diff --git a/tools/perf/util/dso.c b/tools/perf/util/dso.c
index dc9b495..b9e087f 100644
--- a/tools/perf/util/dso.c
+++ b/tools/perf/util/dso.c
@@ -32,6 +32,7 @@ char dso__symtab_origin(const struct dso *dso)
 		[DSO_BINARY_TYPE__JAVA_JIT]			= 'j',
 		[DSO_BINARY_TYPE__DEBUGLINK]			= 'l',
 		[DSO_BINARY_TYPE__BUILD_ID_CACHE]		= 'B',
+		[DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO]	= 'D',
 		[DSO_BINARY_TYPE__FEDORA_DEBUGINFO]		= 'f',
 		[DSO_BINARY_TYPE__UBUNTU_DEBUGINFO]		= 'u',
 		[DSO_BINARY_TYPE__OPENEMBEDDED_DEBUGINFO]	= 'o',
@@ -97,7 +98,12 @@ int dso__read_binary_type_filename(const struct dso *dso,
 		break;
 	}
 	case DSO_BINARY_TYPE__BUILD_ID_CACHE:
-		if (dso__build_id_filename(dso, filename, size) == NULL)
+		if (dso__build_id_filename(dso, filename, size, false) == NULL)
+			ret = -1;
+		break;
+
+	case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
+		if (dso__build_id_filename(dso, filename, size, true) == NULL)
 			ret = -1;
 		break;
 
diff --git a/tools/perf/util/dso.h b/tools/perf/util/dso.h
index 78ec637..f886141 100644
--- a/tools/perf/util/dso.h
+++ b/tools/perf/util/dso.h
@@ -21,6 +21,7 @@ enum dso_binary_type {
 	DSO_BINARY_TYPE__JAVA_JIT,
 	DSO_BINARY_TYPE__DEBUGLINK,
 	DSO_BINARY_TYPE__BUILD_ID_CACHE,
+	DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO,
 	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
 	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
 	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
diff --git a/tools/perf/util/machine.c b/tools/perf/util/machine.c
index 246b441..a54a2be 100644
--- a/tools/perf/util/machine.c
+++ b/tools/perf/util/machine.c
@@ -705,7 +705,8 @@ size_t machine__fprintf_vmlinux_path(struct machine *machine, FILE *fp)
 
 	if (kdso->has_build_id) {
 		char filename[PATH_MAX];
-		if (dso__build_id_filename(kdso, filename, sizeof(filename)))
+		if (dso__build_id_filename(kdso, filename, sizeof(filename),
+					   false))
 			printed += fprintf(fp, "[0] %s\n", filename);
 	}
 
diff --git a/tools/perf/util/symbol.c b/tools/perf/util/symbol.c
index 8c7bae5..971b990 100644
--- a/tools/perf/util/symbol.c
+++ b/tools/perf/util/symbol.c
@@ -53,6 +53,7 @@ static enum dso_binary_type binary_type_symtab[] = {
 	DSO_BINARY_TYPE__JAVA_JIT,
 	DSO_BINARY_TYPE__DEBUGLINK,
 	DSO_BINARY_TYPE__BUILD_ID_CACHE,
+	DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO,
 	DSO_BINARY_TYPE__FEDORA_DEBUGINFO,
 	DSO_BINARY_TYPE__UBUNTU_DEBUGINFO,
 	DSO_BINARY_TYPE__BUILDID_DEBUGINFO,
@@ -1418,6 +1419,7 @@ static bool dso__is_compatible_symtab_type(struct dso *dso, bool kmod,
 		return kmod && dso->symtab_type == type;
 
 	case DSO_BINARY_TYPE__BUILD_ID_CACHE:
+	case DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO:
 		return true;
 
 	case DSO_BINARY_TYPE__NOT_FOUND:
@@ -1565,10 +1567,14 @@ int dso__load(struct dso *dso, struct map *map)
 		struct symsrc *ss = &ss_[ss_pos];
 		bool next_slot = false;
 		bool is_reg;
+		bool nsexit;
 		int sirc;
 
 		enum dso_binary_type symtab_type = binary_type_symtab[i];
 
+		nsexit = (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE ||
+		    symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE_DEBUGINFO);
+
 		if (!dso__is_compatible_symtab_type(dso, kmod, symtab_type))
 			continue;
 
@@ -1576,13 +1582,13 @@ int dso__load(struct dso *dso, struct map *map)
 						   root_dir, name, PATH_MAX))
 			continue;
 
-		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		if (nsexit)
 			nsinfo__mountns_exit(&nsc);
 
 		is_reg = is_regular_file(name);
 		sirc = symsrc__init(ss, dso, name, symtab_type);
 
-		if (symtab_type == DSO_BINARY_TYPE__BUILD_ID_CACHE)
+		if (nsexit)
 			nsinfo__mountns_enter(dso->nsinfo, &nsc);
 
 		if (!is_reg || sirc < 0) {
@@ -1724,7 +1730,7 @@ int dso__load_vmlinux_path(struct dso *dso, struct map *map)
 	}
 
 	if (!symbol_conf.ignore_vmlinux_buildid)
-		filename = dso__build_id_filename(dso, NULL, 0);
+		filename = dso__build_id_filename(dso, NULL, 0, false);
 	if (filename != NULL) {
 		err = dso__load_vmlinux(dso, map, filename, true);
 		if (err > 0)

^ permalink raw reply related	[flat|nested] 42+ messages in thread

* Re: [PATCH v2 tip/perf/core 5/6] perf top: support lookup of symbols in other mount namespaces.
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 5/6] perf top: support lookup of symbols in other mount namespaces Krister Johansen
@ 2017-07-21 17:10           ` Arnaldo Carvalho de Melo
  2017-07-26 17:23           ` [tip:perf/core] perf top: Support " tip-bot for Krister Johansen
  1 sibling, 0 replies; 42+ messages in thread
From: Arnaldo Carvalho de Melo @ 2017-07-21 17:10 UTC (permalink / raw)
  To: Krister Johansen
  Cc: Brendan Gregg, Peter Zijlstra, Ingo Molnar, Alexander Shishkin,
	linux-kernel, Thomas-Mich Richter

Em Wed, Jul 05, 2017 at 06:48:12PM -0700, Krister Johansen escreveu:
> The perf top command needs to unshare its fs from the helper threads
> in order to successfully setns(2) during its symbol lookup.  It also
> needs to impelement a force flag to ignore ownership of perf-<pid>.map
> files.

Thanks, applied, this one fell thru the cracks last time.

- Arnaldo

^ permalink raw reply	[flat|nested] 42+ messages in thread

* [tip:perf/core] perf top: Support lookup of symbols in other mount namespaces.
  2017-07-06  1:48         ` [PATCH v2 tip/perf/core 5/6] perf top: support lookup of symbols in other mount namespaces Krister Johansen
  2017-07-21 17:10           ` Arnaldo Carvalho de Melo
@ 2017-07-26 17:23           ` tip-bot for Krister Johansen
  1 sibling, 0 replies; 42+ messages in thread
From: tip-bot for Krister Johansen @ 2017-07-26 17:23 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: linux-kernel, alexander.shishkin, acme, kjlx, tglx, tmricht, hpa,
	peterz, mingo, brendan.d.gregg

Commit-ID:  868a832918f621b7576655c00067f20326ef3931
Gitweb:     http://git.kernel.org/tip/868a832918f621b7576655c00067f20326ef3931
Author:     Krister Johansen <kjlx@templeofstupid.com>
AuthorDate: Wed, 5 Jul 2017 18:48:12 -0700
Committer:  Arnaldo Carvalho de Melo <acme@redhat.com>
CommitDate: Tue, 25 Jul 2017 22:43:16 -0300

perf top: Support lookup of symbols in other mount namespaces.

The perf top command needs to unshare its fs from the helper threads in
order to successfully setns(2) during its symbol lookup.  It also needs
to impelement a force flag to ignore ownership of perf-<pid>.map files.

Signed-off-by: Krister Johansen <kjlx@templeofstupid.com>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Thomas-Mich Richter <tmricht@linux.vnet.ibm.com>
Link: http://lkml.kernel.org/r/1499305693-1599-6-git-send-email-kjlx@templeofstupid.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
---
 tools/perf/Documentation/perf-top.txt |  4 ++++
 tools/perf/builtin-top.c              | 15 +++++++++++++++
 2 files changed, 19 insertions(+)

diff --git a/tools/perf/Documentation/perf-top.txt b/tools/perf/Documentation/perf-top.txt
index e71d638..d864ea6 100644
--- a/tools/perf/Documentation/perf-top.txt
+++ b/tools/perf/Documentation/perf-top.txt
@@ -237,6 +237,10 @@ Default is to monitor all CPUS.
 --hierarchy::
 	Enable hierarchy output.
 
+--force::
+	Don't do ownership validation.
+
+
 INTERACTIVE PROMPTING KEYS
 --------------------------
 
diff --git a/tools/perf/builtin-top.c b/tools/perf/builtin-top.c
index e5a8f24..ee954bd 100644
--- a/tools/perf/builtin-top.c
+++ b/tools/perf/builtin-top.c
@@ -587,6 +587,13 @@ static void *display_thread_tui(void *arg)
 		.refresh	= top->delay_secs,
 	};
 
+	/* In order to read symbols from other namespaces perf to  needs to call
+	 * setns(2).  This isn't permitted if the struct_fs has multiple users.
+	 * unshare(2) the fs so that we may continue to setns into namespaces
+	 * that we're observing.
+	 */
+	unshare(CLONE_FS);
+
 	perf_top__sort_new_samples(top);
 
 	/*
@@ -628,6 +635,13 @@ static void *display_thread(void *arg)
 	struct perf_top *top = arg;
 	int delay_msecs, c;
 
+	/* In order to read symbols from other namespaces perf to  needs to call
+	 * setns(2).  This isn't permitted if the struct_fs has multiple users.
+	 * unshare(2) the fs so that we may continue to setns into namespaces
+	 * that we're observing.
+	 */
+	unshare(CLONE_FS);
+
 	display_setup_sig();
 	pthread__unblock_sigwinch();
 repeat:
@@ -1206,6 +1220,7 @@ int cmd_top(int argc, const char **argv)
 		    "Show raw trace event output (do not use print fmt or plugins)"),
 	OPT_BOOLEAN(0, "hierarchy", &symbol_conf.report_hierarchy,
 		    "Show entries in a hierarchy"),
+	OPT_BOOLEAN(0, "force", &symbol_conf.force, "don't complain, do it"),
 	OPT_END()
 	};
 	const char * const top_usage[] = {

^ permalink raw reply related	[flat|nested] 42+ messages in thread

end of thread, other threads:[~2017-07-26 17:27 UTC | newest]

Thread overview: 42+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-07-01  2:18 [PATCH tip/perf/core 0/7] namespace tracing improvements Krister Johansen
2017-07-01  2:18 ` [PATCH tip/perf/core 1/7] perf symbols: find symbols in different mount namespace Krister Johansen
2017-07-03 18:38   ` Arnaldo Carvalho de Melo
2017-07-05 20:44     ` Krister Johansen
2017-07-01  2:18 ` [PATCH tip/perf/core 2/7] perf maps: lookup maps in both intitial mountns and inner mountns Krister Johansen
2017-07-03 18:44   ` Arnaldo Carvalho de Melo
2017-07-01  2:18 ` [PATCH tip/perf/core 3/7] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
2017-07-03 18:46   ` Arnaldo Carvalho de Melo
2017-07-05 20:45     ` Krister Johansen
2017-07-01  2:18 ` [PATCH tip/perf/core 4/7] perf buildid-cache: support binary objects from other namespaces Krister Johansen
2017-07-01  2:18 ` [PATCH tip/perf/core 5/7] perf top: support lookup of symbols in other mount namespaces Krister Johansen
2017-07-01  2:18 ` [PATCH tip/perf/core 6/7] perf documentation: updates for target-ns Krister Johansen
2017-07-03 18:48   ` Arnaldo Carvalho de Melo
2017-07-05 20:45     ` Krister Johansen
2017-07-06  1:48       ` [PATCH v2 tip/perf/core 0/6] namespace tracing improvements Krister Johansen
2017-07-06  1:48         ` [PATCH v2 tip/perf/core 1/6] perf symbols: find symbols in different mount namespace Krister Johansen
2017-07-06 19:41           ` Arnaldo Carvalho de Melo
2017-07-07 19:36             ` Krister Johansen
2017-07-10  6:17               ` Thomas-Mich Richter
2017-07-10 22:39                 ` Krister Johansen
2017-07-10 22:52                   ` Arnaldo Carvalho de Melo
2017-07-10 23:29                     ` Krister Johansen
2017-07-11 12:51                       ` Arnaldo Carvalho de Melo
2017-07-11 17:15                         ` Krister Johansen
2017-07-20  8:48           ` [tip:perf/core] perf symbols: Find " tip-bot for Krister Johansen
2017-07-06  1:48         ` [PATCH v2 tip/perf/core 2/6] perf maps: lookup maps in both intitial mountns and inner mountns Krister Johansen
2017-07-20  8:48           ` [tip:perf/core] perf maps: Lookup " tip-bot for Krister Johansen
2017-07-06  1:48         ` [PATCH v2 tip/perf/core 3/6] perf probe: allow placing uprobes in alternate namespaces Krister Johansen
2017-07-17 10:32           ` [PATCH] perf probe: Fix build failure for get_target_map() Ravi Bangoria
2017-07-17 13:11             ` Arnaldo Carvalho de Melo
2017-07-18  9:19               ` Michael Ellerman
2017-07-18 15:45                 ` Arnaldo Carvalho de Melo
2017-07-19  5:58                   ` Michael Ellerman
2017-07-20  8:48           ` [tip:perf/core] perf probe: Allow placing uprobes in alternate namespaces tip-bot for Krister Johansen
2017-07-06  1:48         ` [PATCH v2 tip/perf/core 4/6] perf buildid-cache: support binary objects from other namespaces Krister Johansen
2017-07-20  8:49           ` [tip:perf/core] perf buildid-cache: Support " tip-bot for Krister Johansen
2017-07-06  1:48         ` [PATCH v2 tip/perf/core 5/6] perf top: support lookup of symbols in other mount namespaces Krister Johansen
2017-07-21 17:10           ` Arnaldo Carvalho de Melo
2017-07-26 17:23           ` [tip:perf/core] perf top: Support " tip-bot for Krister Johansen
2017-07-06  1:48         ` [PATCH v2 tip/perf/core 6/6] perf buildid-cache: cache debuginfo Krister Johansen
2017-07-20  8:49           ` [tip:perf/core] perf buildid-cache: Cache debuginfo tip-bot for Krister Johansen
2017-07-01  2:18 ` [PATCH tip/perf/core 7/7] perf buildid-cache: cache debuginfo Krister Johansen

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.