diff --git a/observe/lsns.cpp b/observe/lsns.cpp index 6151a6c..1232ed0 100644 --- a/observe/lsns.cpp +++ b/observe/lsns.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ #include #include +#include "lsns.skel.h" + struct ns_key_t { uint32_t type; @@ -351,62 +354,6 @@ static std::unordered_map> 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 &procs, - int pad_width -) -{ - std::unordered_map> children; - std::unordered_map 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 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) { @@ -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() @@ -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; } @@ -575,7 +487,7 @@ 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; } @@ -583,8 +495,7 @@ int main(int argc, char **argv) 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; } @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 objs; objs.reserve(headers.size()); for (const auto &h : headers) @@ -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; }