| Title: A common fatal error and assert fail handler |
| |
| Description: |
| |
| These two common handlers are developed in order to reduce the redundancy |
| code writing for fatal and assert handler for error case testing. They can |
| be used both in kernel and userspace, and they are also SMP safe. |
| |
| |
| Why doing this |
| ============== |
| |
| When writing error testing case (or we call it negative test case), we might |
| have to write self-defined k_sys_fatal_handler or post_assert_handler to deal |
| with the errors we caught, in order to make the test continue. This means much |
| identical code would be written. So we try to move the error handler definition |
| into a common part and let other test suites or applications reuse it, instead |
| of defining their own. |
| |
| And when error happens, we sometimes need a special action to make our testing |
| program return back to normal, such as releasing some resource hold before the |
| error happened. This is why we add a hook on it, in order to achieve that goal. |
| |
| |
| How to use it in you app |
| ======================== |
| |
| (a) Usage for dealing with fatal error: |
| |
| Step1: Add CONFIG_ZTEST_FATAL_HOOK=y into prj.conf |
| |
| Step2: Include <ztest_fatal_hook.h> in C source file. |
| |
| Step3: (optional) Define a hook function call ztest_post_fatal_error_hook(). |
| |
| Step4: Call ztest_set_fault_valid(true) before where your target function |
| call. |
| |
| |
| (b) Usage for dealing with assert fail: |
| |
| Step1: Add CONFIG_ZTEST_ASSERT_HOOK=y into prj.conf |
| |
| Step2: Include <zephyr/ztest_error_hook.h> in your C code. |
| |
| Step3: (optional) Define a hook function call ztest_post_assert_fail_hook(). |
| |
| Step4: call ztest_set_assert_valid(true) before where your target function |
| call. |
| |
| |
| You can choose to use one or both of them, depending on your needs. |
| If you use none of them, you can still define your own fatal or assert handler |
| as usual. |
| |
| |
| Test example in this test set |
| ============================= |
| |
| This test verifies if the common fatal error and assert fail handler works. |
| If the expected error was caught, the test case will pass. |
| |
| test_catch_assert_fail |
| - To call a function then giving the condition to trigger the assert fail, |
| then catch it by the assert handler. |
| |
| test_catch_fatal_error |
| - start a thread to test triggering a null address dereferencing, then catch |
| the (expected) fatal error. |
| - start a thread to test triggering an illegal instruction, then catch |
| the (expected) fatal error. |
| - start a thread to test triggering a divide-by-zero error, then catch |
| the (expected) fatal error. |
| - start a thread to call k_oops() then catch the (expected) fatal error. |
| - start a thread to call k_panel() then catch the (expected) fatal error. |
| |
| test_catch_assert_in_isr |
| - start a thread to enter ISR context by calling irq_offload(), then trigger |
| an assert fail then catch it. |
| |
| test_catch_z_oops |
| - Pass illegal address by syscall, then inside the syscall handler, the |
| K_OOPS macro will trigger a fatal error that will get caught (as expected). |
| |
| |
| |
| Limitation of this usage |
| ======================== |
| |
| Trigger a fatal error in ISR context, that will cause problem due to |
| the interrupt stack is already abnormal when we want to continue other |
| test case, we do not recover it so far. |
| |
| |
| --------------------------------------------------------------------------- |
| |
| Sample Output: |
| |
| Running test suite error_hook_tests |
| =================================================================== |
| START - test_catch_assert_fail |
| ASSERTION FAIL [a != ((void *)0)] @ WEST_TOPDIR/zephyr/tests/ztest/error_hook/src/main.c:41 |
| parameter a should not be NULL! |
| Caught assert failed |
| Assert error expected as part of test case. |
| PASS - test_catch_assert_fail |
| =================================================================== |
| START - test_catch_fatal_error |
| case type is 0 |
| E: Page fault at address (nil) (error code 0x4) |
| E: Linear address not present in page tables |
| E: Access violation: user thread not allowed to read |
| E: PTE: not present |
| E: EAX: 0x00000000, EBX: 0x00000000, ECX: 0x00000000, EDX: 0x0010fe51 |
| E: ESI: 0x00000000, EDI: 0x0012dfe8, EBP: 0x0012dfcc, ESP: 0x0012dfc4 |
| E: EFLAGS: 0x00000246 CS: 0x002b CR3: 0x001142c0 |
| E: call trace: |
| E: EIP: 0x00100439 |
| E: 0x001010ea (0x113068) |
| E: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0 |
| E: Current thread: 0x114000 (unknown) |
| Caught system error -- reason 0 1 |
| Fatal error expected as part of test case. |
| case type is 1 |
| E: Page fault at address 0x12dfc4 (error code 0x15) |
| E: Access violation: user thread not allowed to execute |
| E: PTE: 0x12d000 -> 0x000000000012d000: RW US A D XD |
| E: EAX: 0x0012dfc4, EBX: 0x00000001, ECX: 0x00000001, EDX: 0x0010fe51 |
| E: ESI: 0x00000000, EDI: 0x0012dfe8, EBP: 0x0012dfcc, ESP: 0x0012dfc0 |
| E: EFLAGS: 0x00000246 CS: 0x002b CR3: 0x001142c0 |
| E: call trace: |
| E: EIP: 0x0012dfc4 |
| E: 0x001010ea (0x113068) |
| E: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0 |
| E: Current thread: 0x114000 (unknown) |
| Caught system error -- reason 0 1 |
| Fatal error expected as part of test case. |
| case type is 2 |
| E: Invalid opcode |
| E: EAX: 0x00000000, EBX: 0x00000002, ECX: 0x00000002, EDX: 0x0010fe51 |
| E: ESI: 0x00000000, EDI: 0x0012dfe8, EBP: 0x0012dfcc, ESP: 0x0012dfc4 |
| E: EFLAGS: 0x00000246 CS: 0x002b CR3: 0x001142c0 |
| E: call trace: |
| E: EIP: 0x00100451 |
| E: 0x001010ea (0x113068) |
| E: >>> ZEPHYR FATAL ERROR 0: CPU exception on CPU 0 |
| E: Current thread: 0x114000 (unknown) |
| Caught system error -- reason 0 1 |
| Fatal error expected as part of test case. |
| case type is 3 |
| E: EAX: 0x00000000, EBX: 0x00000003, ECX: 0x00000003, EDX: 0x0010fe51 |
| E: ESI: 0x00000000, EDI: 0x0012dfe8, EBP: 0x0012dfcc, ESP: 0x0012dfc0 |
| E: EFLAGS: 0x00000246 CS: 0x002b CR3: 0x001142c0 |
| E: call trace: |
| E: EIP: 0x0010045c |
| E: 0x001010ea (0x113068) |
| E: >>> ZEPHYR FATAL ERROR 3: Kernel oops on CPU 0 |
| E: Current thread: 0x114000 (unknown) |
| Caught system error -- reason 3 1 |
| Fatal error expected as part of test case. |
| case type is 4 |
| E: EAX: 0x00000000, EBX: 0x00000004, ECX: 0x00000004, EDX: 0x0010fe51 |
| E: ESI: 0x00000000, EDI: 0x0012dfe8, EBP: 0x0012dfcc, ESP: 0x0012dfc0 |
| E: EFLAGS: 0x00000246 CS: 0x002b CR3: 0x001142c0 |
| E: call trace: |
| E: EIP: 0x00100465 |
| E: 0x001010ea (0x113068) |
| E: >>> ZEPHYR FATAL ERROR 3: Kernel oops on CPU 0 |
| E: Current thread: 0x114000 (unknown) |
| Caught system error -- reason 3 1 |
| Fatal error expected as part of test case. |
| PASS - test_catch_fatal_error |
| =================================================================== |
| START - test_catch_assert_in_isr |
| ASSERTION FAIL [a != ((void *)0)] @ WEST_TOPDIR/zephyr/tests/ztest/error_hook/src/main.c:41 |
| parameter a should not be NULL! |
| Caught assert failed |
| Assert error expected as part of test case. |
| PASS - test_catch_assert_in_isr |
| =================================================================== |
| START - test_catch_z_oops |
| E: EAX: 0x00000000, EBX: 0x00000000, ECX: 0x00000000, EDX: 0x00000000 |
| E: ESI: 0x00000000, EDI: 0x00000000, EBP: 0x00000000, ESP: 0x00000000 |
| E: EFLAGS: 0x001003c4 CS: 0x0511 CR3: 0x00115740 |
| E: call trace: |
| E: EIP: 0x0010ff9e |
| E: NULL base ptr |
| E: >>> ZEPHYR FATAL ERROR 3: Kernel oops on CPU 0 |
| E: Current thread: 0x114120 (unknown) |
| Caught system error -- reason 3 1 |
| Fatal error expected as part of test case. |
| PASS - test_catch_z_oops |
| =================================================================== |
| Test suite error_hook_tests succeeded |
| =================================================================== |
| PROJECT EXECUTION SUCCESSFUL |