Re: [PATCH 1/5] perf jitdump: Fix PID namespace detection

Namhyung Kim
2025-11-07T02:16:43Z

On Wed, Nov 05, 2025 at 08:10:24PM +0100, Ilya Leoshkevich wrote:
> perf inject fails to detect jitdump file produced by a process
> running in a different PID namespace if this process has not exited
> yet.
>
> The PID namespace heuristic in jit_detect() compares two PIDs:
>
> * pid: outermost NStgid of mmap(jitdump) caller from perf's PoV.
> * nsinfo__nstgid(nsi): innermost NStgid of mmap(jitdump) caller from
> perf's PoV.
>
> The semantics of the in_pidns variable can be seen in, e.g.,
> nsinfo__get_nspid(): it's true if and only if perf and the profiled
> process are in different PID namespaces.
>
> The current logic is clearly inverted: if pid and nsinfo__nstgid(nsi)
> are different, then the profiled process must be in a different PID
> namespace. This, of course, ignores that fact that they may end up
> being equal by accident, but that's not the point here.
>
> Fix by flipping the comparison.
>
> Changing just that, however, breaks the case when the process has
> exited. Add explicit support for that by adding "synthesized" field to
> nsinfo, which tracks whether NStgid was obtained from a running
> process (ignoring considerations of PID reuse or running inject on
> a different machine). When the namespace information is synthesized,
> assume the process ran in a different PID namespace.

I'm not sure I'm following. It'd be great if anyone understand the
topic well could review.

>
> Signed-off-by: Ilya Leoshkevich <iii@linux.ibm.com>
> ---
> tools/perf/util/jitdump.c | 27 +++++++++++++++++++++------
> tools/perf/util/namespaces.c | 9 +++++++++
> tools/perf/util/namespaces.h | 2 ++
> 3 files changed, 32 insertions(+), 6 deletions(-)
>
> diff --git a/tools/perf/util/jitdump.c b/tools/perf/util/jitdump.c
> index b062b1f234b62..19e4bc139935b 100644
> --- a/tools/perf/util/jitdump.c
> +++ b/tools/perf/util/jitdump.c
> @@ -788,15 +788,30 @@ jit_detect(const char *mmap_name, pid_t pid, struct nsinfo *nsi, bool *in_pidns)
> if (!end)
> return -1;
>
> - *in_pidns = pid == nsinfo__nstgid(nsi);
> /*
> - * pid does not match mmap pid
> - * pid==0 in system-wide mode (synthesized)
> + * Determine whether the process ran inside a container, and whether it
> + * mapped jit.dump for profiling purposes or by accident. Record this
> + * for further use in jit_inject(). The kernel does not provide PID
> + * namespace information, so we have to resort to guesswork.
> *
> - * If the pid in the file name is equal to the nstgid, then
> - * the agent ran inside a container and perf outside the
> - * container, so record it for further use in jit_inject().
> + * If the process exited and perf had to synthesize the namespace
> + * information, then it's not possible to have any certainty; be
> + * aggressive and assume that the process ran inside a container. This
> + * allows the user to proceed with injection at the cost of a small
> + * probability of injecting irrelevant data.
> + *
> + * If the process' NStgid as observed by perf is different from its
> + * innermost NStgid, then it must have run inside a container. There is
> + * a very small probability that NStgids randomly happenned to be the
> + * same; ignore it.
> + *
> + * pid == 0 means system-wide mode, just proceed.
> + *
> + * Finally, the most straightforward case: if the PID in the file name
> + * is equal to the process' NStgid as observed by perf, then it must be
> + * a match.
> */
> + *in_pidns = nsinfo__synthesized(nsi) || pid != nsinfo__nstgid(nsi);
> if (pid && !(pid2 == pid || *in_pidns))
> return -1;
> /*
> diff --git a/tools/perf/util/namespaces.c b/tools/perf/util/namespaces.c
> index 01502570b32d0..7de5d62e271c4 100644
> --- a/tools/perf/util/namespaces.c
> +++ b/tools/perf/util/namespaces.c
> @@ -132,6 +132,8 @@ int nsinfo__init(struct nsinfo *nsi)
>
> rv = nsinfo__get_nspid(&RC_CHK_ACCESS(nsi)->tgid, &RC_CHK_ACCESS(nsi)->nstgid,
> &RC_CHK_ACCESS(nsi)->in_pidns, spath);
> + if (rv == 0)
> + RC_CHK_ACCESS(nsi)->synthesized = false;
>
> out:
> free(newns);
> @@ -166,6 +168,7 @@ struct nsinfo *nsinfo__new(pid_t pid)
> RC_CHK_ACCESS(nsi)->nstgid = pid;
> nsinfo__clear_need_setns(nsi);
> RC_CHK_ACCESS(nsi)->in_pidns = false;
> + RC_CHK_ACCESS(nsi)->synthesized = true;
> /* 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.
> @@ -197,6 +200,7 @@ struct nsinfo *nsinfo__copy(const struct nsinfo *nsi)
> RC_CHK_ACCESS(nnsi)->nstgid = nsinfo__nstgid(nsi);
> RC_CHK_ACCESS(nnsi)->need_setns = nsinfo__need_setns(nsi);
> RC_CHK_ACCESS(nnsi)->in_pidns = nsinfo__in_pidns(nsi);
> + RC_CHK_ACCESS(nnsi)->synthesized = nsinfo__synthesized(nsi);
> if (nsinfo__mntns_path(nsi)) {
> RC_CHK_ACCESS(nnsi)->mntns_path = strdup(nsinfo__mntns_path(nsi));
> if (!RC_CHK_ACCESS(nnsi)->mntns_path) {
> @@ -275,6 +279,11 @@ void nsinfo__set_in_pidns(struct nsinfo *nsi)
> RC_CHK_ACCESS(nsi)->in_pidns = true;
> }
>
> +bool nsinfo__synthesized(const struct nsinfo *nsi)
> +{
> + return RC_CHK_ACCESS(nsi)->synthesized;
> +}
> +
> void nsinfo__mountns_enter(struct nsinfo *nsi,
> struct nscookie *nc)
> {
> diff --git a/tools/perf/util/namespaces.h b/tools/perf/util/namespaces.h
> index e95c79b80e27c..41ba2ea8137e5 100644
> --- a/tools/perf/util/namespaces.h
> +++ b/tools/perf/util/namespaces.h
> @@ -38,6 +38,7 @@ DECLARE_RC_STRUCT(nsinfo) {
> bool in_pidns;
> char *mntns_path;
> refcount_t refcnt;
> + bool synthesized;

It'd be nice if you can put this along with other bool fields.

Thanks,
Namhyung

> };
>
> struct nscookie {
> @@ -60,6 +61,7 @@ pid_t nsinfo__nstgid(const struct nsinfo *nsi);
> pid_t nsinfo__pid(const struct nsinfo *nsi);
> bool nsinfo__in_pidns(const struct nsinfo *nsi);
> void nsinfo__set_in_pidns(struct nsinfo *nsi);
> +bool nsinfo__synthesized(const struct nsinfo *nsi);
>
> void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc);
> void nsinfo__mountns_exit(struct nscookie *nc);
> --
> 2.51.1
>