MCPcopy Create free account
hub / github.com/bloomberg/pystack / frameCallback

Function frameCallback

src/pystack/_pystack/unwinder.cpp:78–140  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

76}
77
78static int
79frameCallback(Dwfl_Frame* state, void* arg)
80{
81 auto* frames = static_cast<std::vector<Frame>*>(arg);
82 Dwarf_Addr pc;
83 bool isActivation;
84 if (!dwfl_frame_pc(state, &pc, &isActivation)) {
85 LOG(DEBUG) << "dwfl_frame_pc failed";
86 return -1;
87 }
88
89 std::optional<Dwarf_Word> stackPointer;
90 // Unwinding through musl libc with elfutils can get stuck returning the
91 // same PC in a loop forever.
92 //
93 // https://sourceware.org/bugzilla/show_bug.cgi?id=30272
94 // https://marc.info/?l=musl&m=168053842303968&w=2
95 //
96 // We can work around this by asking elfutils what the stack pointer is for
97 // each frame and breaking out on our own if two different frames report
98 // the same stack pointer. When PyStack is built with glibc and not built
99 // with a recent enough version of elfutils for us to do this check, we
100 // skip it. This isn't entirely correct, as it means that a PyStack built
101 // with glibc and an old elfutils can fail to collect stacks for Python
102 // interpreters built against musl libc, but it avoids a hard dependency on
103 // a newer version of elfutils than most distros have available. It's
104 // unlikely that users will encounter problems, but if they do, the simple
105 // work around is to install PyStack using the same interpreter they want
106 // to get stacks for, or to build with a more recent version of elfutils.
107
108#if _ELFUTILS_VERSION >= 188 or (defined(__linux__) && !defined(__GLIBC__))
109
110 // These platform specific magic numbers are part of the platform ABI.
111 // For any platform not handled below we never look up the value of the
112 // stack pointer register, and so never return DWARF_CB_ABORT.
113 std::optional<unsigned int> stackPointerRegNo;
114# if defined(__x86_64__)
115 // https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
116 // Figure 3.36: DWARF Register Number Mapping
117 stackPointerRegNo = 7;
118# elif defined(__aarch64__)
119 // https://refspecs.linuxfoundation.org/ELF/ppc64/PPC-elf64abi.html#DW-REG
120 stackPointerRegNo = 31;
121# endif
122
123 if (stackPointerRegNo) {
124 stackPointer.emplace(0);
125 if (0 != dwfl_frame_reg(state, stackPointerRegNo.value(), &stackPointer.value())) {
126 throw UnwinderError("Invalid register number!");
127 }
128 }
129
130 if (!frames->empty() && pc == frames->back().pc && isActivation == frames->back().isActivation
131 && stackPointer && stackPointer == frames->back().stackPointer)
132 {
133 LOG(DEBUG) << std::hex << std::showbase << "Breaking out of (infinite?) unwind loop @ " << pc;
134 return DWARF_CB_ABORT;
135 }

Callers

nothing calls this directly

Calls 2

LOGClass · 0.85
UnwinderErrorClass · 0.85

Tested by

no test coverage detected