pytest_panic.py 12 KB

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