| 76 | } |
| 77 | |
| 78 | static int |
| 79 | frameCallback(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 | } |
nothing calls this directly
no test coverage detected