Skip to content

Commit

Permalink
libbpf-tools: support to find symbols in different mount namespace
Browse files Browse the repository at this point in the history
Signed-off-by: Wenbo Zhang <[email protected]>
  • Loading branch information
ethercflow committed Dec 23, 2023
1 parent 3469bf1 commit e2a380b
Showing 1 changed file with 239 additions and 2 deletions.
241 changes: 239 additions & 2 deletions libbpf-tools/trace_helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
// 28-Feb-2020 Wenbo Zhang Created this.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#include <sched.h>
#endif
#include <ctype.h>
#include <inttypes.h>
Expand All @@ -15,6 +16,7 @@
#include <errno.h>
#include <fcntl.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <time.h>
#include <bpf/bpf.h>
#include <bpf/btf.h>
Expand Down Expand Up @@ -320,6 +322,220 @@ static int get_elf_text_scn_info(const char *path, uint64_t *addr,
return err;
}

/*
* The following code related to nsinfo is based on the code of perf, see:
* https://github.com/torvalds/linux/commit/843ff37bb59edbe51d64e77ba1b3245a15a4dd9f
*/
struct nscookie {
int oldns;
int newns;
char *oldcwd;
};

struct nsinfo {
pid_t pid;
pid_t tgid;
pid_t nstgid;
bool need_setns;
char *mntns_path;
};

static pid_t nsinfo__pid(const struct nsinfo *nsi)
{
return nsi->pid;
}

static inline void nsinfo__clear_need_setns(struct nsinfo *nsi)
{
nsi->need_setns = false;
}

static inline bool nsinfo__need_setns(struct nsinfo *nsi)
{
return nsi->need_setns;
}

static inline const char *nsinfo__mntns_path(const struct nsinfo *nsi)
{
return nsi->mntns_path;
}

static int nsinfo__get_nspid(pid_t *tgid, pid_t *nstgid, const char *path)
{
FILE *f = NULL;
char *statln = NULL;
size_t linesz = 0;
char *nspid;

f = fopen(path, "r");
if (f == NULL)
return -1;

while (getline(&statln, &linesz, f) != -1) {
/* Use tgid if CONFIG_PID_NS is not defined. */
if (strstr(statln, "Tgid:") != NULL) {
*tgid = (pid_t)strtol(strrchr(statln, '\t'), NULL, 10);
*nstgid = *tgid;
}

if (strstr(statln, "NStgid:") != NULL) {
nspid = strrchr(statln, '\t');
*nstgid = (pid_t)strtol(nspid, NULL, 10);
break;
}
}

fclose(f);
free(statln);
return 0;
}

static int nsinfo__init(struct nsinfo *nsi)
{
char oldns[PATH_MAX];
char spath[PATH_MAX];
struct stat old_stat;
struct stat new_stat;
char *newns = NULL;
int rv = -1;

if (snprintf(oldns, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
return rv;

if (asprintf(&newns, "/proc/%d/ns/mnt", nsi->pid) == -1)
return rv;

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;
}

/* 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", nsinfo__pid(nsi)) >= PATH_MAX)
goto out;

rv = nsinfo__get_nspid(&nsi->tgid, &nsi->nstgid, spath);

out:
free(newns);
return rv;
}

static struct nsinfo *nsinfo__new(pid_t pid)
{
struct nsinfo *nsi;

if (pid == 0)
return NULL;

nsi = calloc(1, sizeof(*nsi));
if (!nsi)
return NULL;
nsi->pid = pid;

/* 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) < 0) {
nsinfo__clear_need_setns(nsi);
}

return nsi;
}

static void nsinfo__free(struct nsinfo *nsi)
{
if (nsi) {
free(nsi->mntns_path);
free(nsi);
}
}

static void nsinfo__mountns_enter(struct nsinfo *nsi, struct nscookie *nc)
{
char curpath[PATH_MAX];
int oldns = -1;
int newns = -1;
char *oldcwd = NULL;

if (nc == NULL)
return;

nc->oldns = -1;
nc->newns = -1;

if (!nsi || !nsinfo__need_setns(nsi))
return;

if (snprintf(curpath, PATH_MAX, "/proc/self/ns/mnt") >= PATH_MAX)
return;

oldcwd = get_current_dir_name();
if (!oldcwd)
return;

oldns = open(curpath, O_RDONLY);
if (oldns < 0)
goto errout;

newns = open(nsinfo__mntns_path(nsi), O_RDONLY);
if (newns < 0)
goto errout;

if (setns(newns, CLONE_NEWNS) < 0)
goto errout;

nc->oldcwd = oldcwd;
nc->oldns = oldns;
nc->newns = newns;

return;

errout:
free(oldcwd);
if (oldns > -1)
close(oldns);
if (newns > -1)
close(newns);
}

static void nsinfo__mountns_exit(struct nscookie *nc)
{
if (nc == NULL || nc->oldns == -1 || nc->newns == -1 || !nc->oldcwd)
return;

setns(nc->oldns, CLONE_NEWNS);

if (nc->oldcwd) {
chdir(nc->oldcwd);
free(nc->oldcwd);
nc->oldcwd = NULL;
}

if (nc->oldns > -1) {
close(nc->oldns);
nc->oldns = -1;
}

if (nc->newns > -1) {
close(nc->newns);
nc->newns = -1;
}
}

static int syms__add_dso(struct syms *syms, struct map *map, const char *name)
{
struct dso *dso = NULL;
Expand Down Expand Up @@ -706,12 +922,33 @@ struct syms *syms__load_file(const char *fname)
return NULL;
}

static void fill_map_file_name(char *fname, size_t len, struct nsinfo *nsi)
{
pid_t tgid = nsi->tgid;

if (nsi->need_setns) {
tgid = nsi->nstgid;
}
snprintf(fname, len, "/proc/%ld/maps", (long)tgid);
}

struct syms *syms__load_pid(pid_t tgid)
{
struct nscookie nsc;
struct nsinfo *nsi;
struct syms *syms;
char fname[128];

snprintf(fname, sizeof(fname), "/proc/%ld/maps", (long)tgid);
return syms__load_file(fname);
nsi = nsinfo__new(tgid);
if (!nsi)
return NULL;

nsinfo__mountns_enter(nsi, &nsc);
fill_map_file_name(fname,sizeof(fname), nsi);
syms = syms__load_file(fname);
nsinfo__mountns_exit(&nsc);
nsinfo__free(nsi);
return syms;
}

void syms__free(struct syms *syms)
Expand Down

0 comments on commit e2a380b

Please sign in to comment.