Stateful decoder that watches serial output for ESP32 crash traces. Call ``process_line(line)`` for every serial line. It returns an empty list during normal operation and a list of decoded/formatted strings when a crash dump is complete.
| 68 | |
| 69 | |
| 70 | class CrashTraceDecoder: |
| 71 | """Stateful decoder that watches serial output for ESP32 crash traces. |
| 72 | |
| 73 | Call ``process_line(line)`` for every serial line. It returns an empty list |
| 74 | during normal operation and a list of decoded/formatted strings when a |
| 75 | crash dump is complete. |
| 76 | """ |
| 77 | |
| 78 | def __init__( |
| 79 | self, |
| 80 | build_dir: Path, |
| 81 | environment: str, |
| 82 | *, |
| 83 | use_fbuild: bool = False, |
| 84 | ) -> None: |
| 85 | self._build_dir = build_dir |
| 86 | self._environment = environment |
| 87 | self._use_fbuild = use_fbuild |
| 88 | |
| 89 | # Resolved lazily on first crash. |
| 90 | self._elf_path: Optional[Path] = None |
| 91 | self._addr2line: Optional[Path] = None |
| 92 | self._resolved = False |
| 93 | |
| 94 | # Accumulation state. |
| 95 | self._in_crash = False |
| 96 | self._crash_lines: list[str] = [] |
| 97 | self._grace_count = 0 |
| 98 | self._crash_detected = False # True once we've seen at least one crash |
| 99 | |
| 100 | # ------------------------------------------------------------------ |
| 101 | # Public API |
| 102 | # ------------------------------------------------------------------ |
| 103 | |
| 104 | @property |
| 105 | def crash_detected(self) -> bool: |
| 106 | """Whether at least one crash has been detected so far.""" |
| 107 | return self._crash_detected |
| 108 | |
| 109 | def process_line(self, line: str) -> list[str]: |
| 110 | """Feed a serial line and return any decoded output lines. |
| 111 | |
| 112 | Returns: |
| 113 | Empty list during normal output. When a crash dump ends, returns |
| 114 | a list of formatted strings (banner + decoded trace). |
| 115 | """ |
| 116 | stripped = line.strip() |
| 117 | |
| 118 | if not self._in_crash: |
| 119 | if self._is_crash_start(stripped): |
| 120 | self._in_crash = True |
| 121 | self._crash_detected = True |
| 122 | self._crash_lines = [stripped] |
| 123 | self._grace_count = 0 |
| 124 | return [] |
| 125 | |
| 126 | # Already accumulating a crash. |
| 127 | if self._is_crash_continuation(stripped): |
no outgoing calls
no test coverage detected