Skip to content

Commit 32d6854

Browse files
committed
Resolve Container Native Libraries
- Add a libdwfl fallback that opens absolute module paths through /proc/<pid>/root so remote Pyxis/Enroot targets can resolve libraries that are only visible inside the container filesystem. - Associate the analyzed PID with each DWFL module before attach so the ELF lookup callback can find the target process root during native unwinding. - Preserve the existing build-id and linux-proc lookup paths first; the process-root fallback is used only when normal host lookup fails. - This fixes the behavior seen with the ImageNet Pyxis NCCL run where PyStack 1.6 reported insufficient native information or lost libtorch/libtorch_cuda/NCCL frames from container targets.
1 parent 0e1b1ee commit 32d6854

1 file changed

Lines changed: 63 additions & 2 deletions

File tree

src/pystack/_pystack/elf_common.cpp

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <iomanip>
66
#include <iostream>
77
#include <string>
8+
#include <sys/stat.h>
89
#include <utility>
910

1011
#include "compat.h"
@@ -14,6 +15,58 @@ namespace pystack {
1415

1516
using file_unique_ptr = std::unique_ptr<FILE, std::function<int(FILE*)>>;
1617

18+
namespace {
19+
20+
int
21+
set_process_pid(
22+
Dwfl_Module* mod __attribute__((unused)),
23+
void** userdata,
24+
const char* name __attribute__((unused)),
25+
Dwarf_Addr start __attribute__((unused)),
26+
void* arg)
27+
{
28+
*userdata = arg;
29+
return DWARF_CB_OK;
30+
}
31+
32+
int
33+
find_elf_through_process_root(void** userdata, const char* modname, char** file_name)
34+
{
35+
if (userdata == nullptr || *userdata == nullptr || modname == nullptr || modname[0] != '/') {
36+
return -1;
37+
}
38+
39+
const auto pid = *static_cast<const pid_t*>(*userdata);
40+
if (pid <= 0) {
41+
return -1;
42+
}
43+
44+
const std::string rooted_path =
45+
"/proc/" + std::to_string(pid) + "/root" + std::string(modname);
46+
int fd = open(rooted_path.c_str(), O_RDONLY);
47+
if (fd < 0) {
48+
return -1;
49+
}
50+
51+
struct stat file_stat;
52+
if (fstat(fd, &file_stat) != 0 || !S_ISREG(file_stat.st_mode)) {
53+
close(fd);
54+
return -1;
55+
}
56+
57+
if (file_name != nullptr) {
58+
*file_name = strdup(rooted_path.c_str());
59+
if (*file_name == nullptr) {
60+
close(fd);
61+
return -1;
62+
}
63+
}
64+
65+
return fd;
66+
}
67+
68+
} // namespace
69+
1770
int
1871
pystack_find_elf(
1972
Dwfl_Module* mod,
@@ -31,10 +84,14 @@ pystack_find_elf(
3184
return ret;
3285
}
3386
ret = dwfl_linux_proc_find_elf(mod, userdata, modname, base, file_name, elfp);
34-
if (file_name == nullptr) {
87+
if (ret < 0) {
88+
ret = find_elf_through_process_root(userdata, modname, file_name);
89+
}
90+
if (ret < 0) {
3591
LOG(DEBUG) << "Could not locate debug info for " << the_modname;
3692
} else {
37-
LOG(DEBUG) << "Located debug info for " << the_modname << " by path in " << *file_name;
93+
const char* the_filename = (file_name == nullptr || *file_name == nullptr) ? "???" : *file_name;
94+
LOG(DEBUG) << "Located debug info for " << the_modname << " by path in " << the_filename;
3895
}
3996
return ret;
4097
}
@@ -285,6 +342,10 @@ ProcessAnalyzer::ProcessAnalyzer(pid_t pid)
285342
throw ElfAnalyzerError("Failed to analyze DWARF information for the remote process");
286343
}
287344

345+
if (dwfl_getmodules(d_dwfl.get(), set_process_pid, &d_pid, 0) == -1) {
346+
throw ElfAnalyzerError("Failed to associate DWARF modules with the remote process");
347+
}
348+
288349
if (dwfl_linux_proc_attach(d_dwfl.get(), pid, true) != 0) {
289350
throw ElfAnalyzerError("Could not attach the DWARF process analyzer");
290351
}

0 commit comments

Comments
 (0)