Extract addresses from crash lines and decode via addr2line.
(self)
| 230 | ) |
| 231 | |
| 232 | def _decode(self) -> list[str]: |
| 233 | """Extract addresses from crash lines and decode via addr2line.""" |
| 234 | self._resolve_tools() |
| 235 | |
| 236 | crash_text = "\n".join(self._crash_lines) |
| 237 | addresses = extract_addresses_from_crash_log(crash_text) |
| 238 | |
| 239 | if not addresses: |
| 240 | return self._format_banner( |
| 241 | ["(no decodable addresses found in crash output)"] |
| 242 | ) |
| 243 | |
| 244 | if not self._elf_path or not self._addr2line: |
| 245 | detail = [] |
| 246 | if not self._elf_path: |
| 247 | detail.append("ELF file not found") |
| 248 | if not self._addr2line: |
| 249 | detail.append("addr2line not found") |
| 250 | return self._format_banner( |
| 251 | [f"(cannot decode: {', '.join(detail)})"] |
| 252 | + [f" raw addresses: {' '.join(addresses)}"] |
| 253 | ) |
| 254 | |
| 255 | # Run addr2line. |
| 256 | cmd = [ |
| 257 | str(self._addr2line), |
| 258 | "-e", |
| 259 | str(self._elf_path), |
| 260 | "-f", # function names |
| 261 | "-C", # demangle |
| 262 | ] + addresses |
| 263 | |
| 264 | try: |
| 265 | result = subprocess.run( |
| 266 | cmd, capture_output=True, text=True, timeout=10, check=False |
| 267 | ) |
| 268 | except subprocess.TimeoutExpired: |
| 269 | return self._format_banner(["(addr2line timed out)"]) |
| 270 | except FileNotFoundError: |
| 271 | return self._format_banner([f"(addr2line not found at {self._addr2line})"]) |
| 272 | |
| 273 | if result.returncode != 0: |
| 274 | return self._format_banner([f"(addr2line failed: {result.stderr.strip()})"]) |
| 275 | |
| 276 | # Parse addr2line output (alternating function / location lines). |
| 277 | decoded_lines: list[str] = [] |
| 278 | output_lines = result.stdout.strip().split("\n") |
| 279 | for i in range(0, len(output_lines), 2): |
| 280 | func = output_lines[i] if i < len(output_lines) else "??" |
| 281 | loc = output_lines[i + 1] if i + 1 < len(output_lines) else "??:0" |
| 282 | addr = addresses[i // 2] if i // 2 < len(addresses) else "0x????????" |
| 283 | decoded_lines.append(f"{addr}: {func}") |
| 284 | decoded_lines.append(f" at {loc}") |
| 285 | |
| 286 | return self._format_banner(decoded_lines) |
| 287 | |
| 288 | @staticmethod |
| 289 | def _format_banner(body_lines: list[str]) -> list[str]: |
no test coverage detected