Attempt to get a post-mortem stack trace by running the test under GDB. This is used when a test crashes immediately and timeout-based stack traces won't work. Returns: Optional[str]: GDB output with stack trace, or None if not enabled/failed
(
test_executable: Path, enable_stack_trace: bool
)
| 116 | |
| 117 | |
| 118 | def _dump_post_mortem_stack_trace( |
| 119 | test_executable: Path, enable_stack_trace: bool |
| 120 | ) -> Optional[str]: |
| 121 | """ |
| 122 | Attempt to get a post-mortem stack trace by running the test under GDB. |
| 123 | This is used when a test crashes immediately and timeout-based stack traces won't work. |
| 124 | |
| 125 | Returns: |
| 126 | Optional[str]: GDB output with stack trace, or None if not enabled/failed |
| 127 | """ |
| 128 | if not enable_stack_trace: |
| 129 | return None |
| 130 | |
| 131 | try: |
| 132 | # Create GDB script for running the test and capturing stack trace on crash |
| 133 | with tempfile.NamedTemporaryFile( |
| 134 | mode="w+", delete=False, suffix=".gdb" |
| 135 | ) as gdb_script: |
| 136 | gdb_script.write("set pagination off\n") |
| 137 | gdb_script.write("set confirm off\n") |
| 138 | gdb_script.write("set logging file gdb_output.txt\n") |
| 139 | gdb_script.write("set logging on\n") |
| 140 | gdb_script.write("run --minimal\n") # Run the test with minimal output |
| 141 | gdb_script.write("bt full\n") # Backtrace on crash |
| 142 | gdb_script.write("info registers\n") # Register info |
| 143 | gdb_script.write("x/16i $pc\n") # Disassembly around PC |
| 144 | gdb_script.write("thread apply all bt full\n") # All thread backtraces |
| 145 | gdb_script.write("quit\n") |
| 146 | gdb_script_path = gdb_script.name |
| 147 | |
| 148 | # Run the test under GDB |
| 149 | gdb_command = ["gdb", "-batch", "-x", gdb_script_path, str(test_executable)] |
| 150 | |
| 151 | print(f"Running post-mortem stack trace analysis: {' '.join(gdb_command)}") |
| 152 | |
| 153 | gdb_process = subprocess.Popen( |
| 154 | gdb_command, |
| 155 | stdout=subprocess.PIPE, |
| 156 | stderr=subprocess.STDOUT, |
| 157 | text=True, |
| 158 | cwd=_PROJECT_ROOT, |
| 159 | ) |
| 160 | |
| 161 | gdb_output, _ = gdb_process.communicate(timeout=60) # 1 minute timeout for GDB |
| 162 | |
| 163 | # Clean up GDB script |
| 164 | os.unlink(gdb_script_path) |
| 165 | |
| 166 | if gdb_output and gdb_output.strip(): |
| 167 | return gdb_output |
| 168 | else: |
| 169 | return "GDB completed but produced no output" |
| 170 | |
| 171 | except subprocess.TimeoutExpired: |
| 172 | return "GDB analysis timed out after 60 seconds" |
| 173 | except FileNotFoundError: |
| 174 | return "GDB not found - install GDB to enable stack trace analysis" |
| 175 | except KeyboardInterrupt as ki: |
no test coverage detected