1 /*
2  * Arm SCP/MCP Software
3  * Copyright (c) 2018-2021, Arm Limited and Contributors. All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  */
7 
8 #ifndef FWK_ASSERT_H
9 #define FWK_ASSERT_H
10 
11 #include <fwk_attributes.h>
12 #include <fwk_noreturn.h>
13 
14 #include <assert.h>
15 #include <stdbool.h>
16 
17 /*!
18  * \addtogroup GroupLibFramework
19  * \defgroup GroupAssert Assertion Helpers
20  *
21  * \details The framework provides a number of assertion helpers to aid in
22  *      debugging and documenting code. Most of these trigger a standard library
23  *      assertion in some way, but they differ in when.
24  *
25  *      Choosing the most appropriate assertion helper can be complex, so their
26  *      individual purposes are summarised below:
27  *
28  *      - ::fwk_trap() - it is unsafe to continue at all
29  *      - ::fwk_unreachable() - this code is unreachable in normal
30  *          operation, and it is unsafe to continue
31  *      - ::fwk_unexpected() - this code is unreachable in normal operation,
32  *          but it is safe to continue
33  *      - ::fwk_assert(condition) - this condition holds true in normal
34  *          operation, and it is unsafe to continue
35  *      - ::fwk_check(condition) - this condition holds true in normal
36  *          operation, but it is safe to continue (statements only)
37  *      - ::fwk_expect(condition) - this condition holds true in normal
38  *          operation, but it is safe to continue (branch conditions only)
39  *
40  * \{
41  */
42 
43 /*!
44  * \brief Force a target-dependent abnormal program abort.
45  *
46  * \note The only behaviour guaranteed by this macro is that the program will
47  *      terminate abnormally at the point that this macro is called.
48  */
49 #define fwk_trap() __builtin_trap()
50 
51 /*!
52  * \def fwk_unreachable
53  *
54  * \brief Mark a code path as unreachable.
55  *
56  * \details In debug builds, this macro will trigger an assertion. In release
57  *      builds this code path will be marked as unreachable to the compiler.
58  *
59  *      #### Example
60  *
61  *      \code{.c}
62  *      int do_something(int x) {
63  *          switch (x) {
64  *          case 0:
65  *              return FWK_E_STATE;
66  *
67  *          case 10:
68  *              return FWK_SUCCESS;
69  *
70  *          default:
71  *              fwk_unreachable();
72  *          }
73  *      }
74  *      \endcode
75  *
76  *      In this example, the code will assert in a debug build if the
77  *      unreachable code is reached. In a release build the behaviour of the
78  *      program is undefined.
79  */
80 
81 #if defined(NDEBUG) || defined(__clang_analyzer__)
82 #    define fwk_unreachable() __builtin_unreachable()
83 #else
84 #    define fwk_unreachable() fwk_assert((bool)false)
85 #endif
86 
87 /*!
88  * \def fwk_unexpected
89  *
90  * \brief Mark a code path as unexpected.
91  *
92  * \details Unexpected code paths are paths which the code should never have
93  *      taken, but which have associated error handling.
94  *
95  *      In debug builds, this macro will trigger an assertion. In release
96  *      builds, or if running tests, it will do nothing.
97  *
98  *      #### Example
99  *
100  *      \code{.c}
101  *      if (rand() == 42) {
102  *          fwk_unexpected();
103  *
104  *          return FWK_E_STATE;
105  *      }
106  *
107  *      return FWK_E_SUCCESS;
108  *      \endcode
109  *
110  *      In this example, the code will assert in a debug build if `rand()`
111  *      returns `42`. In a release build the expectation will be removed and the
112  *      function will return `FWK_E_STATE`.
113  */
114 
115 #if defined(NDEBUG) || defined(BUILD_TESTS) || defined(__clang_analyzer__)
116 #    define fwk_unexpected() ((void)0)
117 #else
118 #    define fwk_unexpected() fwk_assert((bool)false)
119 #endif
120 
121 /*!
122  * \def fwk_assert
123  *
124  * \brief Assert an invariant.
125  *
126  * \details This macro will pass the condition to the standard C library's
127  *      `assert()` macro, which will evaluate the condition and abort the
128  *      program if the condition fails.
129  *
130  *      Unlike `assert()`, this macro will _evaluate_ the condition regardless
131  *      of whether assertions are enabled or not.
132  *
133  * \param condition Condition to test.
134  */
135 
136 #ifdef NDEBUG
137 #    if FWK_HAS_BUILTIN(__builtin_assume)
138 #        define fwk_assert(condition) \
139             do { \
140                 bool c = (condition); \
141                 __builtin_assume(c); \
142             } while (0)
143 #    else
144 #        define fwk_assert(condition) ((void)(condition))
145 #    endif
146 #else
147 #    define fwk_assert(condition) assert(condition)
148 #endif
149 
150 /*!
151  * \def fwk_check
152  *
153  * \brief Expect the success of a condition in a statement.
154  *
155  * \details In debug builds, the macro will evaluate the condition and trigger
156  *      an assertion if it fails. In release builds, or if running tests, the
157  *      macro will evaluate the condition and discard its result.
158  *
159  *      #### Example
160  *
161  *      \code{.c}
162  *      fwk_check(*x > 5);
163  *
164  *      *x = 42;
165  *      \endcode
166  *
167  *      In this example, the code will assert in a debug build if `*x` is
168  *      greater than 5. In a release build the check will be removed and `*x`
169  *      will be assigned `42`.
170  *
171  * \note This macro is similar to ::fwk_expect(), but expands to a statement
172  *      rather than an expression, and does not do any branch weighting.
173  *
174  * \param condition Condition to test.
175  */
176 
177 #if defined(NDEBUG) || defined(BUILD_TESTS)
178 #    define fwk_check(condition) ((void)(condition))
179 #else
180 #    define fwk_check(condition) fwk_assert(condition)
181 #endif
182 
183 /*!
184  * \def fwk_expect
185  *
186  * \brief Expect the success of a condition in an expression.
187  *
188  * \details In debug builds, the macro will evaluate the condition and trigger
189  *      an assertion if it fails. In release builds, or if running tests, the
190  *      macro will evaluate the condition and discard its result.
191  *
192  *      #### Example
193  *
194  *      \code{.c}
195  *      if (!fwk_expect(rand() != 42))
196  *          return FWK_E_STATE;
197  *
198  *      return FWK_E_SUCCESS;
199  *      \endcode
200  *
201  *      In this example, the code will assert in a debug build if `rand` is
202  *      equal to 42. In a release build the branch will be taken.
203  *
204  * \param condition Condition to test.
205  *
206  * \return The value of `condition`.
207  */
208 
209 #if defined(NDEBUG) || defined(BUILD_TESTS)
210 #    define fwk_expect(condition) (bool)__builtin_expect((condition), 1)
211 #else
212 #    define fwk_expect(condition) (bool)(fwk_check(condition), 1)
213 #endif
214 
215 /*!
216  * \}
217  */
218 
219 #endif /* FWK_ASSERT_H */
220