Skip to content

Commit 2a958d9

Browse files
committed
feat: support 'skip_decode_panic', split panic decode output and coredump output
1 parent 523c1ed commit 2a958d9

File tree

6 files changed

+88
-24
lines changed

6 files changed

+88
-24
lines changed

pytest-embedded-idf/pytest_embedded_idf/app.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ class IdfApp(App):
3535
'esp32p4',
3636
'esp32h2',
3737
'esp32c61',
38+
'esp32h21',
39+
'esp32h4',
3840
]
3941

4042
FLASH_ARGS_FILENAME = 'flash_args'

pytest-embedded-idf/pytest_embedded_idf/dut.py

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class IdfDut(IdfUnityDutMixin, SerialDut):
2626
Attributes:
2727
target (str): target chip type
2828
skip_check_coredump (bool): skip check core dumped or not while dut teardown if set to True
29+
skip_decode_panic (bool): skip decode panic output or not while dut teardown if set to True
2930
"""
3031

3132
XTENSA_TARGETS = IdfApp.XTENSA_TARGETS
@@ -46,11 +47,12 @@ def __init__(
4647
self,
4748
app: IdfApp,
4849
skip_check_coredump: bool = False,
50+
skip_decode_panic: bool = False,
4951
**kwargs,
5052
) -> None:
5153
self.target = app.target
5254
self.skip_check_coredump = skip_check_coredump
53-
55+
self.skip_decode_panic = skip_decode_panic
5456
super().__init__(app=app, **kwargs)
5557

5658
self._hard_reset_func = self.serial.hard_reset
@@ -76,7 +78,11 @@ def _get_prefix_map_path(self) -> str:
7678
return primary
7779
return fallback
7880

79-
def _check_panic_decode_trigger(self): # type: () -> None
81+
def _decode_panic(self): # type: () -> None
82+
if self.target not in self.RISCV32_TARGETS:
83+
logging.debug('panic decode only supported for riscv32 targets')
84+
return
85+
8086
if not self.app.elf_file:
8187
logging.warning('No elf file found. Skipping decode panic output...')
8288
return
@@ -109,9 +115,11 @@ def _check_panic_decode_trigger(self): # type: () -> None
109115
'bt',
110116
]
111117
try:
112-
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT)
113-
logging.info('\n\nBacktrace:\n')
114-
logging.info(output.decode())
118+
output = subprocess.check_output(cmd, stderr=subprocess.STDOUT).decode('utf-8')
119+
logging.info(f'Backtrace:\n{output}')
120+
with open(self.logfile.replace('dut', 'panic_decoded', 1), 'w') as fw:
121+
fw.write(output)
122+
logging.info(f'Please check decoded panic output file at: {fw.name}')
115123
except subprocess.CalledProcessError as e:
116124
logging.error(f'Failed to decode panic output: {e.output}. Command was: \n{" ".join(cmd)}')
117125
finally:
@@ -137,8 +145,6 @@ def _check_coredump(self) -> None:
137145
Returns:
138146
None
139147
"""
140-
if self.target in self.RISCV32_TARGETS:
141-
self._check_panic_decode_trigger() # need IDF_PATH
142148
if self.app.sdkconfig.get('ESP_COREDUMP_ENABLE_TO_UART', False):
143149
self._dump_b64_coredumps()
144150
elif self.app.sdkconfig.get('ESP_COREDUMP_ENABLE_TO_FLASH', False):
@@ -166,12 +172,12 @@ def _dump_b64_coredumps(self) -> None:
166172
coredump = CoreDump(
167173
chip=self.target,
168174
core=coredump_file.name,
169-
core_format='b64',
170175
prog=self.app.elf_file,
171176
)
172-
with open(os.path.join(self._meta.logdir, f'coredump_output_{i}'), 'w') as fw:
177+
with open(self.logfile.replace('dut', 'coredump', 1), 'w') as fw:
173178
with redirect_stdout(fw):
174179
coredump.info_corefile()
180+
logging.info(f'Please check coredump output file at: {fw.name}')
175181
except Exception as e:
176182
logging.error(f'Error dumping b64 coredump {i} for target: {self.target}: {e}')
177183
finally:
@@ -185,25 +191,25 @@ def _dump_flash_coredump(self) -> None:
185191

186192
from esp_coredump import CoreDump # need IDF_PATH
187193

188-
if self.app.sdkconfig['ESP_COREDUMP_DATA_FORMAT_ELF']:
189-
core_format = 'elf'
190-
elif self.app.sdkconfig['ESP_COREDUMP_DATA_FORMAT_BIN']:
191-
core_format = 'raw'
192-
else:
193-
raise ValueError('Invalid coredump format. Use _parse_b64_coredump for UART')
194-
195-
with self.serial.disable_redirect_thread():
194+
self.serial.close()
195+
with redirect_stdout(self._q):
196196
coredump = CoreDump(
197197
chip=self.target,
198-
core_format=core_format,
199198
port=self.serial.port,
200199
prog=self.app.elf_file,
201200
)
202-
with open(os.path.join(self._meta.logdir, 'coredump_output'), 'w') as fw:
201+
with open(self.logfile.replace('dut', 'coredump', 1), 'w') as fw:
203202
with redirect_stdout(fw):
204203
coredump.info_corefile()
204+
logging.info(f'Please check coredump output file at: {fw.name}')
205205

206206
def close(self) -> None:
207+
if not self.skip_decode_panic:
208+
try:
209+
self._decode_panic()
210+
except Exception as e:
211+
logging.error(f'Error decoding panic output for target: {self.target}: {e}')
212+
207213
if not self.skip_check_coredump:
208214
try:
209215
self._check_coredump()

pytest-embedded-idf/tests/test_idf.py

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -562,13 +562,46 @@ def test_unity_tester_with_linux(dut):
562562

563563

564564
@toolchain_required
565-
def test_check_coredump(testdir, caplog, first_index_of_messages):
565+
@pytest.mark.parametrize(
566+
'coredump_type',
567+
['flash', 'uart'],
568+
)
569+
def test_check_coredump(testdir, coredump_type, caplog, first_index_of_messages):
570+
testdir.makepyfile(rf"""
571+
import pexpect
572+
import pytest
573+
def test_check_coredump_{coredump_type}(dut):
574+
dut.expect(pexpect.TIMEOUT, timeout=3)
575+
""")
576+
577+
result = testdir.runpytest(
578+
'-s',
579+
'--embedded-services',
580+
'esp,idf',
581+
'--app-path',
582+
f'{os.path.join(testdir.tmpdir, f"hello_world_esp32_coredump_{coredump_type}")}',
583+
'--target',
584+
'esp32',
585+
'--log-cli-level',
586+
'INFO',
587+
'-p',
588+
'no:idf-ci',
589+
)
590+
first_index_of_messages(
591+
re.compile('Please check coredump output file at:.+', re.MULTILINE),
592+
caplog.messages,
593+
)
594+
result.assert_outcomes(passed=1)
595+
596+
597+
@toolchain_required
598+
def test_check_panic(testdir, caplog, first_index_of_messages):
566599
testdir.makepyfile(r"""
567600
import pexpect
568601
import pytest
569602
570-
def test_check_coredump(dut):
571-
dut.expect(pexpect.TIMEOUT, timeout=10)
603+
def test_check_panic(dut):
604+
dut.expect(pexpect.TIMEOUT, timeout=3)
572605
""")
573606

574607
result = testdir.runpytest(
@@ -581,9 +614,15 @@ def test_check_coredump(dut):
581614
'esp32c3',
582615
'--log-cli-level',
583616
'INFO',
617+
'-p',
618+
'no:idf-ci',
619+
)
620+
first_index_of_messages(
621+
re.compile(r'Backtrace:\napp_main \(\) at /COMPONENT_MAIN_DIR/hello_world_main.c:17', re.MULTILINE),
622+
caplog.messages,
584623
)
585624
first_index_of_messages(
586-
re.compile(r'app_main \(\) at /COMPONENT_MAIN_DIR/hello_world_main.c:17', re.MULTILINE),
625+
re.compile(r'Please check decoded panic output file at:.+', re.MULTILINE),
587626
caplog.messages,
588627
)
589628

pytest-embedded-serial/pytest_embedded_serial/serial.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,18 +144,22 @@ def close(self):
144144
logging.debug(f'released {port}')
145145

146146
@contextlib.contextmanager
147-
def disable_redirect_thread(self) -> bool:
147+
def disable_redirect_thread(self, kill_port: bool = False) -> bool:
148148
"""
149149
kill the redirect thread, and start a new one after got yield back
150150
151151
Yields:
152152
True if redirect serial thread has been terminated
153153
"""
154154
killed = self.stop_redirect_thread()
155+
if kill_port:
156+
self.proc.close()
155157

156158
yield killed
157159

158160
if killed:
161+
if kill_port:
162+
self._start()
159163
self.start_redirect_thread()
160164

161165

pytest-embedded/pytest_embedded/dut_factory.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ def _fixture_classes_and_options_fn(
128128
confirm_target_elf_sha256,
129129
erase_nvs,
130130
skip_check_coredump,
131+
skip_decode_panic,
131132
openocd_prog_path,
132133
openocd_cli_args,
133134
gdb_prog_path,
@@ -403,6 +404,7 @@ def _fixture_classes_and_options_fn(
403404
kwargs[fixture].update(
404405
{
405406
'skip_check_coredump': skip_check_coredump,
407+
'skip_decode_panic': skip_decode_panic,
406408
}
407409
)
408410
elif 'esp' in _services and 'nuttx' in _services:
@@ -665,6 +667,7 @@ def create(
665667
confirm_target_elf_sha256: bool | None = None,
666668
erase_nvs: bool | None = None,
667669
skip_check_coredump: bool | None = None,
670+
skip_decode_panic: bool | None = None,
668671
openocd_prog_path: str | None = None,
669672
openocd_cli_args: str | None = None,
670673
gdb_prog_path: str | None = None,
@@ -711,6 +714,7 @@ def create(
711714
confirm_target_elf_sha256: Confirm target ELF SHA256.
712715
erase_nvs: Erase NVS flag.
713716
skip_check_coredump: Skip coredump check flag.
717+
skip_decode_panic: TODO
714718
openocd_prog_path: OpenOCD program path.
715719
openocd_cli_args: OpenOCD CLI arguments.
716720
gdb_prog_path: GDB program path.
@@ -778,6 +782,7 @@ def create(
778782
'confirm_target_elf_sha256': confirm_target_elf_sha256,
779783
'erase_nvs': erase_nvs,
780784
'skip_check_coredump': skip_check_coredump,
785+
'skip_decode_panic': skip_decode_panic,
781786
'openocd_prog_path': openocd_prog_path,
782787
'openocd_cli_args': openocd_cli_args,
783788
'gdb_prog_path': gdb_prog_path,

pytest-embedded/pytest_embedded/plugin.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -940,6 +940,13 @@ def skip_check_coredump(request: FixtureRequest) -> bool | None:
940940
return _request_param_or_config_option_or_default(request, 'skip_check_coredump', None)
941941

942942

943+
@pytest.fixture
944+
@multi_dut_argument
945+
def skip_decode_panic(request: FixtureRequest) -> bool | None:
946+
"""Enable parametrization for the same cli option"""
947+
return _request_param_or_config_option_or_default(request, 'skip_decode_panic', None)
948+
949+
943950
########
944951
# jtag #
945952
########
@@ -1092,6 +1099,7 @@ def parametrize_fixtures(
10921099
confirm_target_elf_sha256,
10931100
erase_nvs,
10941101
skip_check_coredump,
1102+
skip_decode_panic,
10951103
openocd_prog_path,
10961104
openocd_cli_args,
10971105
gdb_prog_path,

0 commit comments

Comments
 (0)