pytest_panic.py 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  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. def get_default_backtrace(config: str) -> List[str]:
  17. return [config, 'app_main', 'main_task', 'vPortTaskWrapper']
  18. def common_test(dut: PanicTestDut, config: str, expected_backtrace: Optional[List[str]] = None) -> None:
  19. if 'gdbstub' in config:
  20. dut.expect_exact('Entering gdb stub now.')
  21. dut.start_gdb()
  22. frames = dut.gdb_backtrace()
  23. if not dut.match_backtrace(frames, expected_backtrace):
  24. raise AssertionError(
  25. 'Unexpected backtrace in test {}:\n{}'.format(config, pformat(frames))
  26. )
  27. dut.revert_log_level()
  28. return
  29. if 'uart' in config:
  30. dut.process_coredump_uart()
  31. elif 'flash' in config:
  32. dut.process_coredump_flash()
  33. elif 'panic' in config:
  34. pass
  35. dut.expect('Rebooting...')
  36. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  37. @pytest.mark.generic
  38. def test_task_wdt(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  39. dut.expect_test_func_name(test_func_name)
  40. dut.expect_exact(
  41. 'Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:'
  42. )
  43. dut.expect_exact('CPU 0: main')
  44. dut.expect(r'abort\(\) was called at PC [0-9xa-f]+ on core 0')
  45. dut.expect_none('register dump:')
  46. dut.expect_backtrace()
  47. dut.expect_elf_sha256()
  48. dut.expect_none('Guru Meditation')
  49. if config == 'gdbstub':
  50. common_test(
  51. dut,
  52. config,
  53. expected_backtrace=[
  54. # Backtrace interrupted when abort is called, IDF-842
  55. 'panic_abort',
  56. 'esp_system_abort',
  57. ],
  58. )
  59. else:
  60. common_test(dut, config)
  61. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  62. @pytest.mark.generic
  63. def test_int_wdt(
  64. dut: PanicTestDut, target: str, config: str, test_func_name: str
  65. ) -> None:
  66. dut.expect_test_func_name(test_func_name)
  67. dut.expect_gme('Interrupt wdt timeout on CPU0')
  68. dut.expect_reg_dump(0)
  69. dut.expect_backtrace()
  70. if target == 'esp32s2':
  71. dut.expect_elf_sha256()
  72. dut.expect_none('Guru Meditation')
  73. if target != 'esp32s2': # esp32s2 is single-core
  74. dut.expect_reg_dump(1)
  75. dut.expect_backtrace()
  76. dut.expect_elf_sha256()
  77. dut.expect_none('Guru Meditation')
  78. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  79. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  80. @pytest.mark.generic
  81. def test_int_wdt_cache_disabled(
  82. dut: PanicTestDut, target: str, config: str, test_func_name: str
  83. ) -> None:
  84. dut.expect_test_func_name(test_func_name)
  85. dut.expect_gme('Interrupt wdt timeout on CPU0')
  86. dut.expect_reg_dump(0)
  87. dut.expect_backtrace()
  88. if target == 'esp32s2':
  89. dut.expect_elf_sha256()
  90. dut.expect_none('Guru Meditation')
  91. if target != 'esp32s2': # esp32s2 is single-core
  92. dut.expect_reg_dump(1)
  93. dut.expect_backtrace()
  94. dut.expect_elf_sha256()
  95. dut.expect_none('Guru Meditation')
  96. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  97. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  98. @pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='raised IllegalInstruction instead')
  99. @pytest.mark.generic
  100. def test_cache_error(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  101. dut.expect_test_func_name(test_func_name)
  102. dut.expect_gme('Cache disabled but cached memory region accessed')
  103. dut.expect_reg_dump(0)
  104. dut.expect_backtrace()
  105. dut.expect_elf_sha256()
  106. dut.expect_none('Guru Meditation')
  107. common_test(
  108. dut, config, expected_backtrace=['die'] + get_default_backtrace(test_func_name)
  109. )
  110. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  111. @pytest.mark.generic
  112. def test_stack_overflow(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  113. dut.expect_test_func_name(test_func_name)
  114. dut.expect_gme('Unhandled debug exception')
  115. dut.expect_exact('Stack canary watchpoint triggered (main)')
  116. dut.expect_reg_dump(0)
  117. dut.expect_backtrace()
  118. dut.expect_elf_sha256()
  119. dut.expect_none('Guru Meditation')
  120. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  121. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  122. @pytest.mark.generic
  123. def test_instr_fetch_prohibited(
  124. dut: PanicTestDut, config: str, test_func_name: str
  125. ) -> None:
  126. dut.expect_test_func_name(test_func_name)
  127. dut.expect_gme('InstrFetchProhibited')
  128. dut.expect_reg_dump(0)
  129. dut.expect_backtrace()
  130. dut.expect_elf_sha256()
  131. dut.expect_none('Guru Meditation')
  132. common_test(
  133. dut,
  134. config,
  135. expected_backtrace=['_init'] + get_default_backtrace(test_func_name),
  136. )
  137. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  138. @pytest.mark.generic
  139. def test_illegal_instruction(
  140. dut: PanicTestDut, config: str, test_func_name: str
  141. ) -> None:
  142. dut.expect_test_func_name(test_func_name)
  143. dut.expect_gme('IllegalInstruction')
  144. dut.expect_reg_dump(0)
  145. dut.expect_backtrace()
  146. dut.expect_elf_sha256()
  147. dut.expect_none('Guru Meditation')
  148. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  149. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  150. @pytest.mark.generic
  151. def test_storeprohibited(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  152. dut.expect_test_func_name(test_func_name)
  153. dut.expect_gme('StoreProhibited')
  154. dut.expect_reg_dump(0)
  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.generic
  161. def test_abort(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  162. dut.expect_test_func_name(test_func_name)
  163. dut.expect(r'abort\(\) was called at PC [0-9xa-f]+ on core 0')
  164. dut.expect_backtrace()
  165. dut.expect_elf_sha256()
  166. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  167. if config == 'gdbstub':
  168. common_test(
  169. dut,
  170. config,
  171. expected_backtrace=[
  172. # Backtrace interrupted when abort is called, IDF-842
  173. 'panic_abort',
  174. 'esp_system_abort',
  175. ],
  176. )
  177. else:
  178. common_test(dut, config)
  179. @pytest.mark.parametrize('config', CONFIGS, indirect=True)
  180. @pytest.mark.generic
  181. def test_ub(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  182. dut.expect_test_func_name(test_func_name)
  183. dut.expect('Undefined behavior of type out_of_bounds')
  184. dut.expect_backtrace()
  185. dut.expect_elf_sha256()
  186. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  187. if config == 'gdbstub':
  188. common_test(
  189. dut,
  190. config,
  191. expected_backtrace=[
  192. # Backtrace interrupted when abort is called, IDF-842
  193. 'panic_abort',
  194. 'esp_system_abort',
  195. ],
  196. )
  197. else:
  198. common_test(dut, config)
  199. #########################
  200. # for config panic only #
  201. #########################
  202. @pytest.mark.esp32
  203. @pytest.mark.esp32s2
  204. @pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='raised IllegalInstruction instead')
  205. @pytest.mark.parametrize('config', ['panic'], indirect=True)
  206. @pytest.mark.generic
  207. def test_abort_cache_disabled(
  208. dut: PanicTestDut, config: str, test_func_name: str
  209. ) -> None:
  210. dut.expect_test_func_name(test_func_name)
  211. dut.expect(r'abort\(\) was called at PC [0-9xa-f]+ on core 0')
  212. dut.expect_backtrace()
  213. dut.expect_elf_sha256()
  214. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  215. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  216. @pytest.mark.esp32
  217. @pytest.mark.esp32s2
  218. @pytest.mark.parametrize('config', ['panic'], indirect=True)
  219. @pytest.mark.generic
  220. def test_assert(dut: PanicTestDut, config: str, test_func_name: str) -> None:
  221. dut.expect_test_func_name(test_func_name)
  222. dut.expect(
  223. re.compile(
  224. rb'assert failed:[\s\w()]*?\s[.\w/]*\.(?:c|cpp|h|hpp):\d.*$', re.MULTILINE
  225. )
  226. )
  227. dut.expect_backtrace()
  228. dut.expect_elf_sha256()
  229. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  230. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))
  231. @pytest.mark.esp32
  232. @pytest.mark.esp32s2
  233. @pytest.mark.xfail('config.getvalue("target") == "esp32s2"', reason='raised IllegalInstruction instead')
  234. @pytest.mark.parametrize('config', ['panic'], indirect=True)
  235. @pytest.mark.generic
  236. def test_assert_cache_disabled(
  237. dut: PanicTestDut, config: str, test_func_name: str
  238. ) -> None:
  239. dut.expect_test_func_name(test_func_name)
  240. dut.expect(re.compile(rb'assert failed: [0-9xa-fA-F]+.*$', re.MULTILINE))
  241. dut.expect_backtrace()
  242. dut.expect_elf_sha256()
  243. dut.expect_none(['Guru Meditation', 'Re-entered core dump'])
  244. common_test(dut, config, expected_backtrace=get_default_backtrace(test_func_name))