.. _assert: Assertions ########## Zephyr provides several assertion facilities for catching programming errors: - **Runtime assertions** check a condition while the code is running and, if it fails, induce a :ref:`fatal error `. The recommended API is the module-aware ``ZASSERT()`` macro; the older ``__ASSERT()`` macro is now a thin compatibility layer on top of it and is deprecated. - **Build assertions** (``BUILD_ASSERT()``) are evaluated entirely at compile-time and always checked. .. note:: The runtime ``ZASSERT()`` macro documented here is unrelated to the lowercase ``zassert_*`` macros (``zassert_true()``, ``zassert_equal()``, ...) provided by the :ref:`Ztest ` framework. The ``zassert_*`` macros report test failures, the ``ZASSERT()`` macro raises a fatal error when a programming error is detected. Runtime Assertions ****************** ZASSERT() ========= The module-aware assertion API is declared in :zephyr_file:`include/zephyr/sys/zassert.h`. Each source file opts into an assertion *module* whose level is a compile-time constant. Because the level is known at compile time, the compiler can optimize the footprint of the assertion code based on the associated assertion level of the module the assert belongs to. Assertion Levels ---------------- Every module resolves to one of four levels: - ``ZASSERT_LEVEL_OFF`` -- assertions are compiled out entirely. - ``ZASSERT_LEVEL_TERSE`` -- assertions are checked; on failure only a fixed ``ASSERTION FAIL`` banner is reported. The location, condition, message and arguments are not compiled in. - ``ZASSERT_LEVEL_NORMAL`` -- assertions are checked; on failure only the location is reported (``ASSERTION FAIL @ file:line``). The condition, message and arguments are not compiled in. - ``ZASSERT_LEVEL_VERBOSE`` -- assertions are checked; on failure the stringified condition, location and optional message are reported. :kconfig:option:`CONFIG_ASSERT` is the master switch. When it is disabled, every module is forced to ``ZASSERT_LEVEL_OFF`` and all ``ZASSERT()`` / ``ZASSERT_MODULE()`` usage compiles to nothing, regardless of any module's configured level. :note: ``ZASSERTS`` footprint reduction relies on compiler optimizations to prune unused conditions, arguments, and string literals at compile time. Lower optimization levels may prevent dead-code elimination, leaving assertion artifacts in the binary despite a low module assertion setting. Selecting a Module ------------------ Place :c:macro:`ZASSERT_MODULE` once at file scope, before any use of :c:macro:`ZASSERT` in the translation unit: .. code-block:: c #include ZASSERT_MODULE(MYMODULE); The module name is an UPPERCASE identifier. Its default level is taken from the Kconfig symbol ``CONFIG_ASSERT_MODULE__LEVEL`` (here ``CONFIG_ASSERT_MODULE_MYMODULE_LEVEL``). A file may override the module default by passing an explicit level as a second argument, for example ``ZASSERT_MODULE(MYMODULE, ZASSERT_LEVEL_VERBOSE)``. Once a module is selected, use :c:macro:`ZASSERT` like a conditional check with an optional :c:func:`printf`-like message: .. code-block:: c ZASSERT(x == 3, "x was %d, expected 3", x); If the condition is false and the module's level is at least ``ZASSERT_LEVEL_TERSE``, a fatal error is raised. The location is only compiled in and printed at ``ZASSERT_LEVEL_NORMAL`` or above, and the condition, message and its arguments are only compiled in and printed at ``ZASSERT_LEVEL_VERBOSE``. For headers and inline functions, avoid ``ZASSERT_MODULE()`` at file scope, as the selection would leak into every file that includes the header. Use one of the following instead. Place :c:macro:`ZASSERT_MODULE` inside the function body. The selection is then block-scoped and does not escape to the includer, and plain :c:macro:`ZASSERT` works within that function: .. code-block:: c static inline void f(void *ptr) { ZASSERT_MODULE(MYMODULE); ZASSERT(ptr != NULL, "ptr must not be NULL"); } For an assertion with a fixed level, use :c:macro:`ZASSERT_TERSE`, :c:macro:`ZASSERT_NORMAL` or :c:macro:`ZASSERT_VERBOSE`. These forms select the level at the call site and declare nothing in scope: .. code-block:: c ZASSERT_TERSE(ptr != NULL); ZASSERT_NORMAL(ptr != NULL); ZASSERT_VERBOSE(ptr != NULL, "ptr must not be NULL"); The ``ZASSERT_LEVEL_TERSE``, ``ZASSERT_LEVEL_NORMAL`` and ``ZASSERT_LEVEL_VERBOSE`` defines configure module assertion levels, while ``ZASSERT_TERSE()``, ``ZASSERT_NORMAL()`` and ``ZASSERT_VERBOSE()`` perform fixed-level assertions. .. note:: A few rules apply to ``ZASSERT()`` and its file-scope module: - ``ZASSERT_MODULE()`` must appear before the first ``ZASSERT()`` in the translation unit, and only one module may be selected per file. - Using ``ZASSERT()`` with no module in scope is a compile error. Select a module first, or use the fixed-level ``ZASSERT_TERSE()``, ``ZASSERT_NORMAL()`` or ``ZASSERT_VERBOSE()`` form. - :kconfig:option:`CONFIG_ASSERT` remains the master switch: when it is disabled the module level is forced to ``ZASSERT_LEVEL_OFF`` regardless of the configured level. Defining a Module's Kconfig Level --------------------------------- The ``CONFIG_ASSERT_MODULE__LEVEL`` symbol is generated from the template :zephyr_file:`subsys/debug/zassert/Kconfig.template.assert`. Source it from a Kconfig file, setting the module name and a human-readable description first: .. code-block:: kconfig module = MYMODULE module-str = the MYMODULE assert module source "subsys/debug/zassert/Kconfig.template.assert" This produces a user-facing ``Off`` / ``Terse`` / ``Normal`` / ``Verbose`` choice and the derived, non-assignable integer symbol ``CONFIG_ASSERT_MODULE_MYMODULE_LEVEL`` consumed by ``ZASSERT_MODULE(MYMODULE)``. The choice defaults to ``Verbose`` and stays overridable from :file:`prj.conf`. Example ------- The :zephyr:code-sample:`assert` sample demonstrates enabling verbose assertions for a single file while the master assertion switch is enabled. A condensed version: .. code-block:: c #include #include ZASSERT_MODULE(MYMODULE); int main(void) { int x = 2; ZASSERT(x == 3, "x was %d, expected 3", x); return 0; } With ``CONFIG_ASSERT_MODULE_MYMODULE_LEVEL_VERBOSE=y`` the failing check produces: .. code-block:: none ASSERTION FAIL [x == 3] @ .../src/main.c:... x was 2, expected 3 Customizing the Failure Behavior -------------------------------- The entire assertion cold path is consolidated into a small set of weak, overridable functions declared in :zephyr_file:`include/zephyr/sys/zassert.h` and implemented in :zephyr_file:`subsys/debug/zassert/zassert.c`: - :c:func:`zassert_fail` reports a failed assertion (location, and when a message is present the message and its arguments) and then invokes :c:func:`zassert_post_action`. Overriding it is the single surface for capturing or redirecting the whole assertion output. - :c:func:`zassert_post_action` takes the terminal action. The default implementation invokes :c:func:`k_oops` if the failing thread was running in user mode, and :c:func:`k_panic` otherwise. - :c:func:`zassert_vprint` is the single primitive through which all assertion text flows. Override it to capture or redirect every assertion message from one place. :c:func:`zassert_print` is a variadic convenience wrapper around it, used by the legacy ``__ASSERT_PRINT()`` compatibility shims. When :kconfig:option:`CONFIG_ASSERT_TEST` is enabled, the post action handler is allowed to return (rather than abort) so that tests can validate assertion behavior by installing a custom hook. Build Assertions **************** Zephyr provides a macro for performing build-time assertion checks. It is evaluated completely at compile-time and always checked. BUILD_ASSERT() ============== This has the same semantics as C's ``_Static_assert`` or C++'s ``static_assert``. If the evaluation fails, a build error will be generated by the compiler. If the compiler supports it, the provided message will be printed to provide further context. Unlike ``__ASSERT()``, the message must be a static string or string concatenation of static strings. The macro does not support formatting or variable arguments. For example, suppose this check fails: .. code-block:: c BUILD_ASSERT(FOO == 2000, "Invalid value of FOO, expected 2000, got " STRINGIFY(FOO)); With GCC, the output resembles: .. code-block:: none tests/kernel/fatal/src/main.c: In function 'test_main': include/zephyr/toolchain/gcc.h:28:37: error: static assertion failed: "Invalid value of FOO, expected 2000, got 1000" #define BUILD_ASSERT(EXPR, MSG) _Static_assert(EXPR, "" MSG) ^~~~~~~~~~~~~~ tests/kernel/fatal/src/main.c:370:2: note: in expansion of macro 'BUILD_ASSERT' BUILD_ASSERT(FOO == 2000, ^~~~~~~~~~~~~~~~ Legacy __ASSERT() ================= The ``__ASSERT()`` family, declared in :zephyr_file:`include/zephyr/sys/__assert.h`, predates ``ZASSERT()`` and is now a compatibility shim using the built-in ``DEFAULT`` assertion module. New code should prefer ``ZASSERT()`` with a dedicated module. .. note:: ``__ASSERT()`` and the ``CONFIG_ASSERT*`` Kconfig options are deprecated. They continue to work through the ``DEFAULT`` module, but the underlying assert API may change in future releases. The ``DEFAULT`` module is enabled by :kconfig:option:`CONFIG_ASSERT`. Its level is controlled by :kconfig:option:`CONFIG_ASSERT_MODULE_DEFAULT_LEVEL`, configured through the ``Off`` / ``Terse`` / ``Normal`` / ``Verbose`` choice (:kconfig:option:`CONFIG_ASSERT_MODULE_DEFAULT_LEVEL_OFF` / ``CONFIG_ASSERT_MODULE_DEFAULT_LEVEL_TERSE`` / ``CONFIG_ASSERT_MODULE_DEFAULT_LEVEL_NORMAL`` / :kconfig:option:`CONFIG_ASSERT_MODULE_DEFAULT_LEVEL_VERBOSE`). Assertions are enabled by default when running Zephyr test cases, as configured by the :kconfig:option:`CONFIG_TEST` option. The deprecated legacy symbols are still honored and derive the ``DEFAULT`` module level: :kconfig:option:`CONFIG_ASSERT_VERBOSE` maps to ``Verbose``, :kconfig:option:`CONFIG_ASSERT_NO_COND_INFO`, :kconfig:option:`CONFIG_ASSERT_NO_MSG_INFO` and :kconfig:option:`CONFIG_ASSERT_NO_FILE_INFO` map to ``Terse``, and a :kconfig:option:`CONFIG_ASSERT_LEVEL` ``== 0`` leaves it ``Off``. When none of these are set, the ``DEFAULT`` module choice falls through to its own :kconfig:option:`CONFIG_ASSERT_MODULE_DEFAULT_LEVEL_VERBOSE` default, preserving the legacy behavior of verbose assertions as the default. To disable all assertions regardless of how the level was configured, set ``CONFIG_ASSERT=n``. API Reference ************* .. doxygengroup:: zassert