pytest_panic.py 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. # SPDX-FileCopyrightText: 2022 Espressif Systems (Shanghai) CO LTD
  2. # SPDX-License-Identifier: CC0-1.0
  3. import re
  4. from pprint import pformat
  5. from typing import List, Optional
  6. import pexpect
  7. import pytest
  8. from test_panic_util import PanicTestDut
  9. CONFIGS = [
  10. pytest.param('coredump_flash_bin_crc', marks=[pytest.mark.esp32, pytest.mark.esp32s2]),
  11. pytest.param('coredump_flash_elf_sha', marks=[pytest.mark.esp32]), # sha256 only supported on esp32
  12. pytest.param('coredump_uart_bin_crc', marks=[pytest.mark.esp32, pytest.mark.esp32s2]),
  13. pytest.param('coredump_uart_elf_crc', marks=[pytest.mark.esp32, pytest.mark.esp32s2]),
  14. pytest.param('gdbstub', marks=[pytest.mark.esp32, pytest.mark.esp32s2]),
  15. pytest.param('panic', marks=[pytest.mark.esp32, pytest.mark.esp32s2]),
  16. ]
  17. # An ESP32-only config, used for tests requiring two cores
  18. CONFIGS_ESP32 = [
  19. pytest.param('coredump_flash_bin_crc', marks=[pytest.mark.esp32]),
  20. pytest.param('coredump_flash_elf_sha', marks=[pytest.mark.esp32]),
  21. pytest.param('coredump_uart_bin_crc', marks=[pytest.mark.esp32]),
  22. pytest.param('coredump_uart_elf_crc', marks=[pytest.mark.esp32]),
  23. pytest.param('gdbstub', marks=[pytest.mark.esp32]),
  24. pytest.param('panic', marks=[pytest.mark.esp32]),
  25. ]
  26. def get_default_backtrace(config: str) -> List[str]:
  27. return [config, 'app_main', 'main_task', 'vPortTaskWrapper']
  28. def common_test(dut: PanicTestDut, config: str, expected_backtrace: Optional[List[str]] = None) -> None:
  29. if 'gdbstub' in config:
  30. dut.expect_exact('Entering gdb stub now.')
  31. dut.start_gdb()
  32. frames = dut.gdb_backtrace()
  33. # Make sure frames and the expected_backtrace have the same size, else, an exception will occur
  34. if expected_backtrace is not None:
  35. size = min(len(frames), len(expected_backtrace))
  36. frames = frames[0:size]
  37. expected_backtrace = expected_backtrace[0:size]
  38. if not dut.match_backtrace(frames, expected_backtrace):
  39. raise AssertionError(
  40. 'Unexpected backtrace in test {}:\n{}'.format(config, pformat(frames))
  41. )
  42. dut.revert_log_level()
  43. return
  44. if 'uart' in config:
  45. dut.process_coredump_uart()
  46. elif 'flash' in config:
  47. dut.process_coredump_flash()
  48. elif 'panic' in config:
  49. pass
  50. dut.expect('Rebooting...')
  51. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  52. @pytest.mark.generic
  53. def test_task_wdt_cpu0(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  54. dut.expect_test_func_name(test_func_name)
  55. dut.expect_exact(
  56. 'Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:'
  57. )
  58. dut.expect_exact('CPU 0: main')
  59. dut.expect_none('register dump:')
  60. dut.expect_exact('Print CPU 0 (current core) backtrace')
  61. dut.expect_backtrace()
  62. dut.expect_elf_sha256()
  63. dut.expect_none('Guru Meditation')
  64. if config == 'gdbstub':
  65. common_test(
  66. dut,
  67. config,
  68. expected_backtrace=[
  69. 'test_task_wdt_cpu0',
  70. 'app_main'
  71. ],
  72. )
  73. else:
  74. common_test(dut, config)
  75. @pytest.mark.parametrize('config', CONFIGS_ESP32, indirect=True)
  76. @pytest.mark.generic
  77. def test_task_wdt_cpu1(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  78. dut.expect_test_func_name(test_func_name)
  79. dut.expect_exact(
  80. 'Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:'
  81. )
  82. dut.expect_exact('CPU 1: Infinite loop')
  83. dut.expect_none('register dump:')
  84. dut.expect_exact('Print CPU 1 backtrace')
  85. dut.expect_backtrace()
  86. dut.expect_elf_sha256()
  87. dut.expect_none('Guru Meditation')
  88. if config == 'gdbstub':
  89. common_test(
  90. dut,
  91. config,
  92. expected_backtrace=[
  93. 'infinite_loop'
  94. ],
  95. )
  96. else:
  97. common_test(dut, config)
  98. @pytest.mark.parametrize('config', CONFIGS_ESP32, indirect=True)
  99. @pytest.mark.generic
  100. def test_task_wdt_both_cpus(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  101. dut.expect_test_func_name(test_func_name)
  102. dut.expect_exact(
  103. 'Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:'
  104. )
  105. dut.expect_exact('CPU 0: Infinite loop')
  106. dut.expect_exact('CPU 1: Infinite loop')
  107. dut.expect_none('register dump:')
  108. dut.expect_exact('Print CPU 0 (current core) backtrace')
  109. dut.expect_backtrace()
  110. dut.expect_exact('Print CPU 1 backtrace')
  111. dut.expect_backtrace()
  112. dut.expect_elf_sha256()
  113. dut.expect_none('Guru Meditation')
  114. if config == 'gdbstub':
  115. common_test(
  116. dut,
  117. config,
  118. expected_backtrace=[
  119. 'infinite_loop'
  120. ],
  121. )
  122. else:
  123. common_test(dut, config)
  124. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  125. @pytest.mark.generic
  126. def test_int_wdt(
  127. dut: PanicTestDut, target: str, config: str, test_func_name: str
  128. ) -> None:
  129. dut.expect_test_func_name(test_func_name)
  130. dut.expect_gme('Interrupt wdt timeout on CPU0')
  131. dut.expect_reg_dump(0)
  132. dut.expect_backtrace()
  133. if target == 'esp32s2':
  134. dut.expect_elf_sha256()
  135. dut.expect_none('Guru Meditation')
  136. if target != 'esp32s2': # esp32s2 is single-core
  137. dut.expect_reg_dump(1)
  138. dut.expect_backtrace()
  139. dut.expect_elf_sha256()
  140. dut.expect_none('Guru Meditation')
  141. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  142. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  143. @pytest.mark.generic
  144. def test_int_wdt_cache_disabled(
  145. dut: PanicTestDut, target: str, config: str, test_func_name: str
  146. ) -> None:
  147. dut.expect_test_func_name(test_func_name)
  148. dut.expect_gme('Interrupt wdt timeout on CPU0')
  149. dut.expect_reg_dump(0)
  150. dut.expect_backtrace()
  151. if target == 'esp32s2':
  152. dut.expect_elf_sha256()
  153. dut.expect_none('Guru Meditation')
  154. if target != 'esp32s2': # esp32s2 is single-core
  155. dut.expect_reg_dump(1)
  156. dut.expect_backtrace()
  157. dut.expect_elf_sha256()
  158. dut.expect_none('Guru Meditation')
  159. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  160. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  161. @pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='raised IllegalInstruction instead')
  162. @pytest.mark.generic
  163. def test_cache_error(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  164. dut.expect_test_func_name(test_func_name)
  165. dut.expect_gme('Cache disabled but cached memory region accessed')
  166. dut.expect_reg_dump(0)
  167. dut.expect_backtrace()
  168. dut.expect_elf_sha256()
  169. dut.expect_none('Guru Meditation')
  170. common_test(
  171. dut, config, expected_backtrace=['die'] + get_default_backtrace(test_func_name)
  172. )
  173. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  174. @pytest.mark.generic
  175. def test_stack_overflow(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  176. dut.expect_test_func_name(test_func_name)
  177. dut.expect_gme('Unhandled debug exception')
  178. dut.expect_exact('Stack canary watchpoint triggered (main)')
  179. dut.expect_reg_dump(0)
  180. dut.expect_backtrace()
  181. dut.expect_elf_sha256()
  182. dut.expect_none('Guru Meditation')
  183. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  184. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  185. @pytest.mark.generic
  186. def test_instr_fetch_prohibited(
  187. dut: PanicTestDut, config: str, test_func_name: str
  188. ) -> None:
  189. dut.expect_test_func_name(test_func_name)
  190. dut.expect_gme('InstrFetchProhibited')
  191. dut.expect_reg_dump(0)
  192. dut.expect_backtrace()
  193. dut.expect_elf_sha256()
  194. dut.expect_none('Guru Meditation')
  195. common_test(
  196. dut,
  197. config,
  198. expected_backtrace=['_init'] + get_default_backtrace(test_func_name),
  199. )
  200. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  201. @pytest.mark.generic
  202. def test_illegal_instruction(
  203. dut: PanicTestDut, config: str, test_func_name: str
  204. ) -> None:
  205. dut.expect_test_func_name(test_func_name)
  206. dut.expect_gme('IllegalInstruction')
  207. dut.expect_reg_dump(0)
  208. dut.expect_backtrace()
  209. dut.expect_elf_sha256()
  210. dut.expect_none('Guru Meditation')
  211. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  212. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  213. @pytest.mark.generic
  214. def test_storeprohibited(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  215. dut.expect_test_func_name(test_func_name)
  216. dut.expect_gme('StoreProhibited')
  217. dut.expect_reg_dump(0)
  218. dut.expect_backtrace()
  219. dut.expect_elf_sha256()
  220. dut.expect_none('Guru Meditation')
  221. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  222. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  223. @pytest.mark.generic
  224. def test_abort(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  225. dut.expect_test_func_name(test_func_name)
  226. dut.expect(r'abort\(\) was called at PC [0-9xa-f]+ on core 0')
  227. dut.expect_backtrace()
  228. dut.expect_elf_sha256()
  229. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  230. if config == 'gdbstub':
  231. common_test(
  232. dut,
  233. config,
  234. expected_backtrace=[
  235. 'panic_abort',
  236. 'esp_system_abort',
  237. 'abort'
  238. ] + get_default_backtrace(test_func_name),
  239. )
  240. else:
  241. common_test(dut, config)
  242. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  243. @pytest.mark.generic
  244. def test_ub(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  245. dut.expect_test_func_name(test_func_name)
  246. dut.expect('Undefined behavior of type out_of_bounds')
  247. dut.expect_backtrace()
  248. dut.expect_elf_sha256()
  249. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  250. if config == 'gdbstub':
  251. common_test(
  252. dut,
  253. config,
  254. expected_backtrace=[
  255. 'panic_abort',
  256. 'esp_system_abort',
  257. '__ubsan_default_handler',
  258. '__ubsan_handle_out_of_bounds'
  259. ] + get_default_backtrace(test_func_name),
  260. )
  261. else:
  262. common_test(dut, config)
  263. #########################
  264. # for config panic only #
  265. #########################
  266. @pytest.mark.esp32
  267. @pytest.mark.esp32s2
  268. @pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='raised IllegalInstruction instead')
  269. @pytest.mark.parametrize('config', ['panic'], indirect=True)
  270. @pytest.mark.generic
  271. def test_abort_cache_disabled(
  272. dut: PanicTestDut, config: str, test_func_name: str
  273. ) -> None:
  274. dut.expect_test_func_name(test_func_name)
  275. dut.expect(r'abort\(\) was called at PC [0-9xa-f]+ on core 0')
  276. dut.expect_backtrace()
  277. dut.expect_elf_sha256()
  278. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  279. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  280. @pytest.mark.esp32
  281. @pytest.mark.esp32s2
  282. @pytest.mark.parametrize('config', ['panic'], indirect=True)
  283. @pytest.mark.generic
  284. def test_assert(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  285. dut.expect_test_func_name(test_func_name)
  286. dut.expect(
  287. re.compile(
  288. rb'assert failed:[\s\w()]*?\s[.\w/]*\.(?:c|cpp|h|hpp):\d.*$', re.MULTILINE
  289. )
  290. )
  291. dut.expect_backtrace()
  292. dut.expect_elf_sha256()
  293. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  294. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  295. @pytest.mark.esp32
  296. @pytest.mark.esp32s2
  297. @pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='raised IllegalInstruction instead')
  298. @pytest.mark.parametrize('config', ['panic'], indirect=True)
  299. @pytest.mark.generic
  300. def test_assert_cache_disabled(
  301. dut: PanicTestDut, config: str, test_func_name: str
  302. ) -> None:
  303. dut.expect_test_func_name(test_func_name)
  304. dut.expect(re.compile(rb'assert failed: [0-9xa-fA-F]+.*$', re.MULTILINE))
  305. dut.expect_backtrace()
  306. dut.expect_elf_sha256()
  307. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  308. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  309. @pytest.mark.esp32
  310. @pytest.mark.parametrize('config', ['panic_delay'], indirect=True)
  311. @pytest.mark.generic
  312. def test_panic_delay(dut: PanicTestDut) -> None:
  313. dut.expect_test_func_name('test_storeprohibited')
  314. try:
  315. dut.expect_exact('Rebooting...', timeout=4)
  316. except pexpect.TIMEOUT:
  317. # We are supposed to NOT find the output for the specified time
  318. pass
  319. else:
  320. # If we actually match the output within the timeout, it means the delay didn't work
  321. raise AssertionError('Rebooted too early, delay is too short')
  322. dut.expect_exact('Rebooting...', timeout=3)
  323. dut.expect_exact('rst:0xc (SW_CPU_RESET)')