Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
195 changes: 50 additions & 145 deletions observe/lsns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
#include <atomic>
#include <signal.h>
#include <sys/resource.h>
#include <sys/ioctl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
Expand All @@ -32,6 +33,8 @@
#include <bpf/bpf.h>
#include <pwd.h>

#include "lsns.skel.h"

struct ns_key_t
{
uint32_t type;
Expand Down Expand Up @@ -351,62 +354,6 @@ static std::unordered_map<std::string, std::vector<ProcInfo>> scan_procs_by_ns()
}
return m;
}

// Print process tree rooted at owner_pid using the given proc list mapped by
// pid
static void print_tree_aligned(
int owner_pid,
const std::vector<ProcInfo> &procs,
int pad_width
)
{
std::unordered_map<int, std::vector<int>> children;
std::unordered_map<int, std::string> cmd;
for (const auto &p : procs)
{
cmd[p.pid] = p.cmd;
children[p.ppid].push_back(p.pid);
}
for (auto &kv : children)
{
std::sort(kv.second.begin(), kv.second.end());
}

// recursive helper defined as a static function to avoid std::function
// overhead
std::function<void(int, const std::string &, bool)> printer =
[&](int pid, const std::string &prefix, bool is_last)
{
std::string line = prefix + (is_last ? "└─" : "├─") +
(cmd.count(pid) ? cmd[pid] : std::to_string(pid));
std::cout << std::left << std::setw(pad_width) << "" << line << "\n";
auto it = children.find(pid);
if (it == children.end())
{
return;
}
const auto &ch = it->second;
for (size_t i = 0; i < ch.size(); ++i)
{
bool last = (i + 1 == ch.size());
std::string child_prefix = prefix + (is_last ? " " : "│ ");
printer(ch[i], child_prefix, last);
}
};

// start from children of owner_pid (owner already printed as header)
auto it = children.find(owner_pid);
if (it == children.end())
{
return;
}
const auto &root_children = it->second;
for (size_t i = 0; i < root_children.size(); ++i)
{
bool last = (i + 1 == root_children.size());
printer(root_children[i], std::string(), last);
}
}
// return a human display name for the namespace type
static const char *ns_display_name(uint32_t t)
{
Expand Down Expand Up @@ -465,45 +412,32 @@ static const char *ns_proc_name(uint32_t t)
}
}

// try to find a pid that owns the namespace in /proc
static int
find_owner_pid_for_ns(uint64_t inum, const char *nstype, time_t *ctime_out)
static int get_terminal_width()
{
DIR *d = opendir("/proc");
if (!d)
struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0)
{
return -1;
return ws.ws_col;
}
struct dirent *de;
char path[256];
struct stat st;
int found = -1;
while ((de = readdir(d)) != NULL)
const char *cols = getenv("COLUMNS");
if (cols)
{
// some filesystems return DT_UNKNOWN; don't rely on d_type
// skip non-numeric
char *endptr;
long pid = strtol(de->d_name, &endptr, 10);
if (*endptr != '\0')
{
continue;
}
snprintf(path, sizeof(path), "/proc/%s/ns/%s", de->d_name, nstype);
if (stat(path, &st) == 0)
int w = atoi(cols);
if (w > 0)
{
if ((uint64_t)st.st_ino == inum)
{
found = (int)pid;
if (ctime_out)
{
*ctime_out = st.st_ctime;
}
break;
}
return w;
}
}
closedir(d);
return found;
return 80;
}

static std::string truncate_cmd(const std::string &s, int max_width)
{
if (max_width <= 0 || (int)s.size() <= max_width)
{
return s;
}
return s.substr(0, max_width);
}

static int bump_memlock_rlimit()
Expand All @@ -527,45 +461,23 @@ int main(int argc, char **argv)

libbpf_set_print(NULL);

const char *candidates[] = {
"bpf/observe/lsns.bpf.o",
"bpf/build/observe/lsns.bpf.o",
"/usr/lib/dkapture/lsns.bpf.o",
NULL
};
parse_args(argc, argv);
register_signal();
struct bpf_object *obj = nullptr;
int err = 0;
for (const char **p = candidates; *p; ++p)
{
obj = bpf_object__open_file(*p, NULL);
if (obj)
{
if ((err = bpf_object__load(obj)) == 0)
{
std::cerr << "loaded BPF object: " << *p << "\n";
break;
}
bpf_object__close(obj);
obj = nullptr;
}
}
if (!obj)
struct lsns_bpf *skel = lsns_bpf__open_and_load();
if (!skel)
{
std::cerr << "failed to open/load BPF object. build the .bpf.o "
"first.\n";
std::cerr << "Try: make -C bpf/observe && make -C observe" << std::endl;
std::cerr << "failed to open/load lsns BPF object\n";
return 1;
}
std::cerr << "loaded BPF object via skeleton\n";

parse_args(argc, argv);
register_signal();

struct bpf_program *prog = skel->progs.iter_tasks;

// find program by name (function name in bpf source)
struct bpf_program *prog =
bpf_object__find_program_by_name(obj, "iter_tasks");
if (!prog)
{
std::cerr << "failed to find iter_tasks program in object\n";
bpf_object__close(obj);
lsns_bpf__destroy(skel);
return 1;
}

Expand All @@ -575,16 +487,15 @@ int main(int argc, char **argv)
if (!link)
{
std::cerr << "failed to attach iterator program\n";
bpf_object__close(obj);
lsns_bpf__destroy(skel);
return 1;
}

int iter_fd = bpf_iter_create(bpf_link__fd(link));
if (iter_fd < 0)
{
std::cerr << "bpf_iter_create failed: " << strerror(errno) << "\n";
bpf_link__destroy(link);
bpf_object__close(obj);
lsns_bpf__destroy(skel);
return 1;
}

Expand All @@ -596,22 +507,11 @@ int main(int argc, char **argv)
}
close(iter_fd);

// fetch map
struct bpf_map *map = bpf_object__find_map_by_name(obj, "ns_map");
if (!map)
{
std::cerr << "failed to find ns_map in object\n";
bpf_link__destroy(link);
bpf_object__close(obj);
return 1;
}
int map_fd = bpf_map__fd(map);

struct bpf_map *cnt_map = bpf_object__find_map_by_name(obj, "ns_cnt_map");
int map_fd = bpf_map__fd(skel->maps.ns_map);
int cnt_map_fd = -1;
if (cnt_map)
if (skel->maps.ns_cnt_map)
{
cnt_map_fd = bpf_map__fd(cnt_map);
cnt_map_fd = bpf_map__fd(skel->maps.ns_cnt_map);
std::cerr << "found per-cpu ns_cnt_map\n";
}
else
Expand Down Expand Up @@ -683,19 +583,22 @@ int main(int argc, char **argv)
// column. This matches the original lsns: every namespace header is printed
// (we do not print extra tree-only rows) and the PATH column shows tree
// prefixes for representative nodes.
const int NS_type = 16;
const int TYPE_type = 18;
const int NS_type = 12;
const int TYPE_type = 8;
const int PROCS_type = 8;
const int USER_type = 20;
const int PID_type = 12;
const int USER_type = 18;
const int PID_type = 8;
const int pad_width =
NS_type + TYPE_type + PROCS_type + USER_type + PID_type;
const int term_width = get_terminal_width();
const int cmd_max_width = term_width - pad_width;

// print header: NS, TYPE, PROCS, USER, PID, COMMAND
std::cout << std::left << std::setw(NS_type) << "NS" << std::setw(TYPE_type)
<< "TYPE" << std::setw(PROCS_type) << "PROCS"
<< std::setw(USER_type) << "USER" << std::setw(PID_type) << "PID"
<< "COMMAND" << "\n";
<< "COMMAND"
<< "\n";

// First pass: collect header info and representatives (do not print yet)
struct HeaderInfo
Expand Down Expand Up @@ -1063,6 +966,7 @@ int main(int argc, char **argv)
{
path_field = rep_prefix[h.rep_tgid] + h.owner_cmd;
}
path_field = truncate_cmd(path_field, cmd_max_width);
std::cout << std::left << std::setw(NS_type) << h.e.inum
<< std::setw(TYPE_type) << h.display
<< std::setw(PROCS_type) << procs_field
Expand Down Expand Up @@ -1091,6 +995,7 @@ int main(int argc, char **argv)
{
path_field = rep_prefix[h.rep_tgid] + h.owner_cmd;
}
path_field = truncate_cmd(path_field, cmd_max_width);
std::cout << std::left << std::setw(NS_type) << h.e.inum
<< std::setw(TYPE_type) << h.display
<< std::setw(PROCS_type) << procs_field
Expand Down Expand Up @@ -1119,6 +1024,7 @@ int main(int argc, char **argv)
hh = hit->second;
}
std::string path = prefix + (is_last ? "└─" : "├─") + cmd;
path = truncate_cmd(path, cmd_max_width);
if (hh)
{
std::string procs_f = hh->total_procs
Expand Down Expand Up @@ -1314,7 +1220,6 @@ int main(int argc, char **argv)
// representative tgid, print the rep tree if it hasn't already been
// printed. For headers without a rep, print the header object directly.
std::cout << "{\n \"namespaces\": [\n";
bool first_obj = true;
std::vector<std::string> objs;
objs.reserve(headers.size());
for (const auto &h : headers)
Expand Down Expand Up @@ -1361,7 +1266,7 @@ int main(int argc, char **argv)
std::cout << "\n ]\n}\n";
}

bpf_link__destroy(link);
bpf_object__close(obj);
lsns_bpf__destroy(skel);

return 0;
}