linux-perf-users.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v3 0/2] perf inject jit: Add namespaces support
@ 2020-11-05  1:48 Yonatan Goldschmidt
  2020-11-05  1:54 ` [PATCH v3 1/2] perf tools: Add 'in_pidns' to nsinfo struct Yonatan Goldschmidt
  2020-11-05  1:56 ` [PATCH v3 2/2] perf inject jit: Add namespaces support Yonatan Goldschmidt
  0 siblings, 2 replies; 3+ messages in thread
From: Yonatan Goldschmidt @ 2020-11-05  1:48 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
	Stephane Eranian, Yonatan Goldschmidt

This version removes some redundant "else"s.

Changlog:
v2 -> v3
* Remove 2 redundant "else"s.

v1 -> v2
* Add 'in_pidns' and use PID & TID from jitdump entries for
  non-containerized processes.

Thanks,

Yonatan Goldschmidt (2):
  perf tools: Add 'in_pidns' to nsinfo struct
  perf inject jit: Add namespaces support

 tools/perf/builtin-inject.c  |  4 +-
 tools/perf/util/jit.h        |  2 +-
 tools/perf/util/jitdump.c    | 84 ++++++++++++++++++++++++++++--------
 tools/perf/util/namespaces.c | 23 +++++++++-
 tools/perf/util/namespaces.h |  3 ++
 5 files changed, 92 insertions(+), 24 deletions(-)

-- 
2.25.0

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

* [PATCH v3 1/2] perf tools: Add 'in_pidns' to nsinfo struct
  2020-11-05  1:48 [PATCH v3 0/2] perf inject jit: Add namespaces support Yonatan Goldschmidt
@ 2020-11-05  1:54 ` Yonatan Goldschmidt
  2020-11-05  1:56 ` [PATCH v3 2/2] perf inject jit: Add namespaces support Yonatan Goldschmidt
  1 sibling, 0 replies; 3+ messages in thread
From: Yonatan Goldschmidt @ 2020-11-05  1:54 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
	Stephane Eranian, Yonatan Goldschmidt

Provides an accurate mean to determine if the owner thread
is in a different PID namespace.

Signed-off-by: Yonatan Goldschmidt <yonatan.goldschmidt@granulate.io>
---
 tools/perf/util/namespaces.c | 11 +++++++++--
 tools/perf/util/namespaces.h |  1 +
 2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index 285d6f30d912..f4b3512d8dd2 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -66,6 +66,7 @@ int nsinfo__init(struct nsinfo *nsi)
 	char spath[PATH_MAX];
 	char *newns = NULL;
 	char *statln = NULL;
+	char *nspid;
 	struct stat old_stat;
 	struct stat new_stat;
 	FILE *f = NULL;
@@ -112,8 +113,12 @@ int nsinfo__init(struct nsinfo *nsi)
 		}
 
 		if (strstr(statln, "NStgid:") != NULL) {
-			nsi->nstgid = (pid_t)strtol(strrchr(statln, '\t'),
-						     NULL, 10);
+			nspid = strrchr(statln, '\t');
+			nsi->nstgid = (pid_t)strtol(nspid, NULL, 10);
+			/* If innermost tgid is not the first, process is in a different
+			 * PID namespace.
+			 */
+			nsi->in_pidns = (statln + sizeof("NStgid:") - 1) != nspid;
 			break;
 		}
 	}
@@ -140,6 +145,7 @@ struct nsinfo *nsinfo__new(pid_t pid)
 		nsi->tgid = pid;
 		nsi->nstgid = pid;
 		nsi->need_setns = false;
+		nsi->in_pidns = false;
 		/* 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.
@@ -166,6 +172,7 @@ struct nsinfo *nsinfo__copy(struct nsinfo *nsi)
 		nnsi->tgid = nsi->tgid;
 		nnsi->nstgid = nsi->nstgid;
 		nnsi->need_setns = nsi->need_setns;
+		nnsi->in_pidns = nsi->in_pidns;
 		if (nsi->mntns_path) {
 			nnsi->mntns_path = strdup(nsi->mntns_path);
 			if (!nnsi->mntns_path) {
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 4b33f684eddd..1cc8637cf885 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -33,6 +33,7 @@ struct nsinfo {
 	pid_t			tgid;
 	pid_t			nstgid;
 	bool			need_setns;
+	bool			in_pidns;
 	char			*mntns_path;
 	refcount_t		refcnt;
 };
-- 
2.25.0

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

* [PATCH v3 2/2] perf inject jit: Add namespaces support
  2020-11-05  1:48 [PATCH v3 0/2] perf inject jit: Add namespaces support Yonatan Goldschmidt
  2020-11-05  1:54 ` [PATCH v3 1/2] perf tools: Add 'in_pidns' to nsinfo struct Yonatan Goldschmidt
@ 2020-11-05  1:56 ` Yonatan Goldschmidt
  1 sibling, 0 replies; 3+ messages in thread
From: Yonatan Goldschmidt @ 2020-11-05  1:56 UTC (permalink / raw)
  To: linux-perf-users, linux-kernel
  Cc: Peter Zijlstra, Ingo Molnar, Arnaldo Carvalho de Melo,
	Mark Rutland, Alexander Shishkin, Jiri Olsa, Namhyung Kim,
	Stephane Eranian, Yonatan Goldschmidt

This patch fixes "perf inject --jit" to properly operate
on namespaced/containerized processes:

* jitdump files are generated by the process, thus they should be
  looked up in its mount NS.
* DSOs of injected MMAP events will later be looked up in the process
  mount NS, so write them into its NS.
* PIDs & TIDs from jitdump events need to be translated to the PID as
  seen by "perf record" before written into MMAP events.

For a process in a different PID NS, the TID & PID given in the jitdump
event are actually ignored; I use the TID & PID of the thread which
mmap()ed the jitdump file. This is simplified and won't do for forks of
the initial process, if they continue using the same jitdump file.
Future patches might improve it.

This was tested by recording a NodeJS process running with "--perf-prof",
inside a Docker container, and by recording another NodeJS process
running in the same namespaces as perf itself, to make sure it's not
broken for non-containerized processes.

Signed-off-by: Yonatan Goldschmidt <yonatan.goldschmidt@granulate.io>
---
 tools/perf/builtin-inject.c  |  4 +-
 tools/perf/util/jit.h        |  2 +-
 tools/perf/util/jitdump.c    | 84 ++++++++++++++++++++++++++++--------
 tools/perf/util/namespaces.c | 12 ++++++
 tools/perf/util/namespaces.h |  2 +
 5 files changed, 82 insertions(+), 22 deletions(-)

diff --git a/tools/perf/builtin-inject.c b/tools/perf/builtin-inject.c
index 6d2f410d773a..715ae87a7368 100644
--- a/tools/perf/builtin-inject.c
+++ b/tools/perf/builtin-inject.c
@@ -292,7 +292,7 @@ static int perf_event__jit_repipe_mmap(struct perf_tool *tool,
 	 * if jit marker, then inject jit mmaps and generate ELF images
 	 */
 	ret = jit_process(inject->session, &inject->output, machine,
-			  event->mmap.filename, event->mmap.pid, &n);
+			  event->mmap.filename, event->mmap.pid, event->mmap.tid, &n);
 	if (ret < 0)
 		return ret;
 	if (ret) {
@@ -330,7 +330,7 @@ static int perf_event__jit_repipe_mmap2(struct perf_tool *tool,
 	 * if jit marker, then inject jit mmaps and generate ELF images
 	 */
 	ret = jit_process(inject->session, &inject->output, machine,
-			  event->mmap2.filename, event->mmap2.pid, &n);
+			  event->mmap2.filename, event->mmap2.pid, event->mmap2.tid, &n);
 	if (ret < 0)
 		return ret;
 	if (ret) {
diff --git a/tools/perf/util/jit.h b/tools/perf/util/jit.h
index 6817ffc2a059..fb810e1b2de7 100644
--- a/tools/perf/util/jit.h
+++ b/tools/perf/util/jit.h
@@ -5,7 +5,7 @@
 #include <data.h>
 
 int jit_process(struct perf_session *session, struct perf_data *output,
-		struct machine *machine, char *filename, pid_t pid, u64 *nbytes);
+		struct machine *machine, char *filename, pid_t pid, pid_t tid, u64 *nbytes);
 
 int jit_inject_record(const char *filename);
 
diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
index 0804308ef285..bdcef7a0d3f5 100644
--- a/tools/perf/util/jitdump.c
+++ b/tools/perf/util/jitdump.c
@@ -18,6 +18,7 @@
 #include "event.h"
 #include "debug.h"
 #include "evlist.h"
+#include "namespaces.h"
 #include "symbol.h"
 #include <elf.h>
 
@@ -35,6 +36,7 @@ struct jit_buf_desc {
 	struct perf_data *output;
 	struct perf_session *session;
 	struct machine *machine;
+	struct nsinfo  *nsi;
 	union jr_entry   *entry;
 	void             *buf;
 	uint64_t	 sample_type;
@@ -72,7 +74,8 @@ struct jit_tool {
 #define get_jit_tool(t) (container_of(tool, struct jit_tool, tool))
 
 static int
-jit_emit_elf(char *filename,
+jit_emit_elf(struct jit_buf_desc *jd,
+	     char *filename,
 	     const char *sym,
 	     uint64_t code_addr,
 	     const void *code,
@@ -83,14 +86,18 @@ jit_emit_elf(char *filename,
 	     uint32_t unwinding_header_size,
 	     uint32_t unwinding_size)
 {
-	int ret, fd;
+	int ret, fd, saved_errno;
+	struct nscookie nsc;
 
 	if (verbose > 0)
 		fprintf(stderr, "write ELF image %s\n", filename);
 
+	nsinfo__mountns_enter(jd->nsi, &nsc);
 	fd = open(filename, O_CREAT|O_TRUNC|O_WRONLY, 0644);
+	saved_errno = errno;
+	nsinfo__mountns_exit(&nsc);
 	if (fd == -1) {
-		pr_warning("cannot create jit ELF %s: %s\n", filename, strerror(errno));
+		pr_warning("cannot create jit ELF %s: %s\n", filename, strerror(saved_errno));
 		return -1;
 	}
 
@@ -99,8 +106,11 @@ jit_emit_elf(char *filename,
 
         close(fd);
 
-        if (ret)
-                unlink(filename);
+	if (ret) {
+		nsinfo__mountns_enter(jd->nsi, &nsc);
+		unlink(filename);
+		nsinfo__mountns_exit(&nsc);
+	}
 
 	return ret;
 }
@@ -134,12 +144,15 @@ static int
 jit_open(struct jit_buf_desc *jd, const char *name)
 {
 	struct jitheader header;
+	struct nscookie nsc;
 	struct jr_prefix *prefix;
 	ssize_t bs, bsz = 0;
 	void *n, *buf = NULL;
 	int ret, retval = -1;
 
+	nsinfo__mountns_enter(jd->nsi, &nsc);
 	jd->in = fopen(name, "r");
+	nsinfo__mountns_exit(&nsc);
 	if (!jd->in)
 		return -1;
 
@@ -367,6 +380,20 @@ jit_inject_event(struct jit_buf_desc *jd, union perf_event *event)
 	return 0;
 }
 
+static pid_t jr_entry_pid(struct jit_buf_desc *jd, union jr_entry *jr)
+{
+	if (jd->nsi && jd->nsi->in_pidns)
+		return jd->nsi->tgid;
+	return jr->load.pid;
+}
+
+static pid_t jr_entry_tid(struct jit_buf_desc *jd, union jr_entry *jr)
+{
+	if (jd->nsi && jd->nsi->in_pidns)
+		return jd->nsi->pid;
+	return jr->load.tid;
+}
+
 static uint64_t convert_timestamp(struct jit_buf_desc *jd, uint64_t timestamp)
 {
 	struct perf_tsc_conversion tc;
@@ -398,14 +425,15 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
 	const char *sym;
 	uint64_t count;
 	int ret, csize, usize;
-	pid_t pid, tid;
+	pid_t nspid, pid, tid;
 	struct {
 		u32 pid, tid;
 		u64 time;
 	} *id;
 
-	pid   = jr->load.pid;
-	tid   = jr->load.tid;
+	nspid = jr->load.pid;
+	pid   = jr_entry_pid(jd, jr);
+	tid   = jr_entry_tid(jd, jr);
 	csize = jr->load.code_size;
 	usize = jd->unwinding_mapped_size;
 	addr  = jr->load.code_addr;
@@ -421,14 +449,14 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
 	filename = event->mmap2.filename;
 	size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%" PRIu64 ".so",
 			jd->dir,
-			pid,
+			nspid,
 			count);
 
 	size++; /* for \0 */
 
 	size = PERF_ALIGN(size, sizeof(u64));
 	uaddr = (uintptr_t)code;
-	ret = jit_emit_elf(filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries,
+	ret = jit_emit_elf(jd, filename, sym, addr, (const void *)uaddr, csize, jd->debug_data, jd->nr_debug_entries,
 			   jd->unwinding_data, jd->eh_frame_hdr_size, jd->unwinding_size);
 
 	if (jd->debug_data && jd->nr_debug_entries) {
@@ -447,7 +475,7 @@ static int jit_repipe_code_load(struct jit_buf_desc *jd, union jr_entry *jr)
 		free(event);
 		return -1;
 	}
-	if (stat(filename, &st))
+	if (nsinfo__stat(filename, &st, jd->nsi))
 		memset(&st, 0, sizeof(st));
 
 	event->mmap2.header.type = PERF_RECORD_MMAP2;
@@ -511,14 +539,15 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
 	int usize;
 	u16 idr_size;
 	int ret;
-	pid_t pid, tid;
+	pid_t nspid, pid, tid;
 	struct {
 		u32 pid, tid;
 		u64 time;
 	} *id;
 
-	pid = jr->move.pid;
-	tid =  jr->move.tid;
+	nspid = jr->load.pid;
+	pid   = jr_entry_pid(jd, jr);
+	tid   = jr_entry_tid(jd, jr);
 	usize = jd->unwinding_mapped_size;
 	idr_size = jd->machine->id_hdr_size;
 
@@ -532,12 +561,12 @@ static int jit_repipe_code_move(struct jit_buf_desc *jd, union jr_entry *jr)
 	filename = event->mmap2.filename;
 	size = snprintf(filename, PATH_MAX, "%s/jitted-%d-%" PRIu64 ".so",
 	         jd->dir,
-	         pid,
+		 nspid,
 		 jr->move.code_index);
 
 	size++; /* for \0 */
 
-	if (stat(filename, &st))
+	if (nsinfo__stat(filename, &st, jd->nsi))
 		memset(&st, 0, sizeof(st));
 
 	size = PERF_ALIGN(size, sizeof(u64));
@@ -696,7 +725,7 @@ jit_inject(struct jit_buf_desc *jd, char *path)
  * as captured in the RECORD_MMAP record
  */
 static int
-jit_detect(char *mmap_name, pid_t pid)
+jit_detect(char *mmap_name, pid_t pid, struct nsinfo *nsi)
  {
 	char *p;
 	char *end = NULL;
@@ -736,7 +765,7 @@ jit_detect(char *mmap_name, pid_t pid)
 	 * pid does not match mmap pid
 	 * pid==0 in system-wide mode (synthesized)
 	 */
-	if (pid && pid2 != pid)
+	if (pid && pid2 != nsi->nstgid)
 		return -1;
 	/*
 	 * validate suffix
@@ -778,16 +807,30 @@ jit_process(struct perf_session *session,
 	    struct machine *machine,
 	    char *filename,
 	    pid_t pid,
+	    pid_t tid,
 	    u64 *nbytes)
 {
+	struct thread *thread;
+	struct nsinfo *nsi;
 	struct evsel *first;
 	struct jit_buf_desc jd;
 	int ret;
 
+	thread = machine__findnew_thread(machine, pid, tid);
+	if (thread == NULL) {
+		pr_err("problem processing JIT mmap event, skipping it.\n");
+		return 0;
+	}
+
+	nsi = nsinfo__get(thread->nsinfo);
+	thread__put(thread);
+
 	/*
 	 * first, detect marker mmap (i.e., the jitdump mmap)
 	 */
-	if (jit_detect(filename, pid)) {
+	if (jit_detect(filename, pid, nsi)) {
+		nsinfo__put(nsi);
+
 		// Strip //anon* mmaps if we processed a jitdump for this pid
 		if (jit_has_pid(machine, pid) && (strncmp(filename, "//anon", 6) == 0))
 			return 1;
@@ -800,6 +843,7 @@ jit_process(struct perf_session *session,
 	jd.session = session;
 	jd.output  = output;
 	jd.machine = machine;
+	jd.nsi = nsi;
 
 	/*
 	 * track sample_type to compute id_all layout
@@ -817,5 +861,7 @@ jit_process(struct perf_session *session,
 		ret = 1;
 	}
 
+	nsinfo__put(jd.nsi);
+
 	return ret;
 }
diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
index f4b3512d8dd2..608b20c72a5c 100644
--- a/tools/perf/util/namespaces.c
+++ b/tools/perf/util/namespaces.c
@@ -287,3 +287,15 @@ char *nsinfo__realpath(const char *path, struct nsinfo *nsi)
 
 	return rpath;
 }
+
+int nsinfo__stat(const char *filename, struct stat *st, struct nsinfo *nsi)
+{
+	int ret;
+	struct nscookie nsc;
+
+	nsinfo__mountns_enter(nsi, &nsc);
+	ret = stat(filename, st);
+	nsinfo__mountns_exit(&nsc);
+
+	return ret;
+}
diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
index 1cc8637cf885..ad9775db7b9c 100644
--- a/tools/perf/util/namespaces.h
+++ b/tools/perf/util/namespaces.h
@@ -8,6 +8,7 @@
 #define __PERF_NAMESPACES_H
 
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <linux/stddef.h>
 #include <linux/perf_event.h>
 #include <linux/refcount.h>
@@ -56,6 +57,7 @@ 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);
+int nsinfo__stat(const char *filename, struct stat *st, struct nsinfo *nsi);
 
 static inline void __nsinfo__zput(struct nsinfo **nsip)
 {
-- 
2.25.0

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

end of thread, other threads:[~2020-11-05  1:56 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2020-11-05  1:48 [PATCH v3 0/2] perf inject jit: Add namespaces support Yonatan Goldschmidt
2020-11-05  1:54 ` [PATCH v3 1/2] perf tools: Add 'in_pidns' to nsinfo struct Yonatan Goldschmidt
2020-11-05  1:56 ` [PATCH v3 2/2] perf inject jit: Add namespaces support Yonatan Goldschmidt

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).