forked from nasa/fprime
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathAssert.hpp
More file actions
207 lines (185 loc) · 7.97 KB
/
Copy pathAssert.hpp
File metadata and controls
207 lines (185 loc) · 7.97 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
#ifndef FW_ASSERT_HPP
#define FW_ASSERT_HPP
#include <Fw/FPrimeBasicTypes.hpp>
// Return only the first argument passed to the macro.
#define FW_ASSERT_FIRST_ARG(ARG_0, ...) ARG_0
// Return all the arguments of the macro, but the first one
#define FW_ASSERT_NO_FIRST_ARG(ARG_0, ...) __VA_ARGS__
// Define FW_UNREACHABLE to hint to the compiler that a code path is unreachable.
// Falls back to a no-op on compilers that do not support __builtin_unreachable.
//
// Two-tier detection:
// 1. __has_builtin — the modern check, available in Clang and GCC >= 10.
// 2. defined(__GNUC__) — catches older GCC (4.5–9) which support the builtin
// but lack __has_builtin. The #ifndef guard ensures only one definition wins.
#ifdef __has_builtin
#if __has_builtin(__builtin_unreachable)
#define FW_UNREACHABLE() __builtin_unreachable()
#endif
#endif
#ifndef FW_UNREACHABLE
#if defined(__GNUC__)
#define FW_UNREACHABLE() __builtin_unreachable()
#else
#define FW_UNREACHABLE() ((void)0) // no-op fallback for unknown compilers
#endif
#endif
// FW_ASSERT_UNREACHABLE is the hint used inside FW_ASSERT macros.
// Only active when FW_ASSERTIONS_ALWAYS_ABORT is enabled, because SwAssert
// can legally return when it is disabled (the default). Using __builtin_unreachable
// after a call that returns is undefined behavior.
#if FW_ASSERTIONS_ALWAYS_ABORT
#define FW_ASSERT_UNREACHABLE() FW_UNREACHABLE()
#else
#define FW_ASSERT_UNREACHABLE() ((void)0)
#endif
#if FW_ASSERT_LEVEL == FW_NO_ASSERT
// Users may override the NO_ASSERT case should they choose
#ifndef FW_ASSERT
#define FW_ASSERT(...) ((void)(FW_ASSERT_FIRST_ARG(__VA_ARGS__)))
#endif
#define FILE_NAME_ARG const CHAR*
#else // ASSERT is defined
// Passing the __LINE__ argument at the end of the function ensures that
// the FW_ASSERT_NO_FIRST_ARG macro will never have an empty variadic variable
#if FW_ASSERT_LEVEL == FW_FILEID_ASSERT && defined ASSERT_FILE_ID
#define FILE_NAME_ARG U32
#define FW_ASSERT(...) \
((FW_ASSERT_FIRST_ARG(__VA_ARGS__, 0)) \
? ((void)0) \
: (Fw::SwAssert(ASSERT_FILE_ID, FW_ASSERT_NO_FIRST_ARG(__VA_ARGS__, __LINE__)), FW_ASSERT_UNREACHABLE()))
#elif FW_ASSERT_LEVEL == FW_FILEID_ASSERT && !defined ASSERT_FILE_ID
#define FILE_NAME_ARG U32
#define FW_ASSERT(...) \
((FW_ASSERT_FIRST_ARG(__VA_ARGS__, 0)) \
? ((void)0) \
: (Fw::SwAssert(static_cast<U32>(0), FW_ASSERT_NO_FIRST_ARG(__VA_ARGS__, __LINE__)), \
FW_ASSERT_UNREACHABLE()))
#elif FW_ASSERT_LEVEL == FW_RELATIVE_PATH_ASSERT && defined ASSERT_RELATIVE_PATH
#define FILE_NAME_ARG const CHAR*
#define FW_ASSERT(...) \
((FW_ASSERT_FIRST_ARG(__VA_ARGS__, 0)) \
? ((void)0) \
: (Fw::SwAssert(ASSERT_RELATIVE_PATH, FW_ASSERT_NO_FIRST_ARG(__VA_ARGS__, __LINE__)), \
FW_ASSERT_UNREACHABLE()))
#else
#define FILE_NAME_ARG const CHAR*
#define FW_ASSERT(...) \
((FW_ASSERT_FIRST_ARG(__VA_ARGS__, 0)) \
? ((void)0) \
: (Fw::SwAssert(__FILE__, FW_ASSERT_NO_FIRST_ARG(__VA_ARGS__, __LINE__)), FW_ASSERT_UNREACHABLE()))
#endif
#endif // if ASSERT is defined
// Helper macro asserting that a value fits into a type without overflow. Helpful for checking before static casts
#define FW_ASSERT_NO_OVERFLOW(value, T) \
FW_ASSERT((value) <= std::numeric_limits<T>::max(), static_cast<FwAssertArgType>(value))
#if FW_ASSERTIONS_ALWAYS_ABORT
#define FW_ASSERT_NORETURN __attribute__((noreturn))
#endif
// F' Assertion functions can technically return even though the intention is for the assertion to terminate the
// program. This breaks static analysis depending on assertions, since the analyzer has to assume the assertion will
// return. When supported, annotate assertion functions as noreturn when statically analyzing.
#ifndef FW_ASSERT_NORETURN
#ifndef __has_feature
#define __has_feature(x) 0 // Compatibility with non-clang compilers.
#endif
#if __has_feature(attribute_analyzer_noreturn)
#define FW_ASSERT_NORETURN __attribute__((analyzer_noreturn))
#else
#define FW_ASSERT_NORETURN
#endif
#endif
// Define NOINLINE as __attribute__((noinline)) if that attribute is available.
// Marking assertion functions as NOINLINE can reduce code size without sacrificing performance
// in the common case that the function is not called.
#ifndef NOINLINE
#ifndef __has_attribute
#define __has_attribute(x) 0
#endif
#if __has_attribute(noinline)
#define NOINLINE __attribute__((noinline))
#else
#define NOINLINE
#endif
#endif
namespace Fw {
//! Assert with no arguments
void SwAssert(FILE_NAME_ARG file, FwSizeType lineNo) NOINLINE FW_ASSERT_NORETURN;
//! Assert with one argument
void SwAssert(FILE_NAME_ARG file, FwAssertArgType arg1, FwSizeType lineNo) NOINLINE FW_ASSERT_NORETURN;
//! Assert with two arguments
void SwAssert(FILE_NAME_ARG file,
FwAssertArgType arg1,
FwAssertArgType arg2,
FwSizeType lineNo) NOINLINE FW_ASSERT_NORETURN;
//! Assert with three arguments
void SwAssert(FILE_NAME_ARG file,
FwAssertArgType arg1,
FwAssertArgType arg2,
FwAssertArgType arg3,
FwSizeType lineNo) NOINLINE FW_ASSERT_NORETURN;
//! Assert with four arguments
void SwAssert(FILE_NAME_ARG file,
FwAssertArgType arg1,
FwAssertArgType arg2,
FwAssertArgType arg3,
FwAssertArgType arg4,
FwSizeType lineNo) NOINLINE FW_ASSERT_NORETURN;
//! Assert with five arguments
void SwAssert(FILE_NAME_ARG file,
FwAssertArgType arg1,
FwAssertArgType arg2,
FwAssertArgType arg3,
FwAssertArgType arg4,
FwAssertArgType arg5,
FwSizeType lineNo) NOINLINE FW_ASSERT_NORETURN;
//! Assert with six arguments
void SwAssert(FILE_NAME_ARG file,
FwAssertArgType arg1,
FwAssertArgType arg2,
FwAssertArgType arg3,
FwAssertArgType arg4,
FwAssertArgType arg5,
FwAssertArgType arg6,
FwSizeType lineNo) NOINLINE FW_ASSERT_NORETURN;
} // namespace Fw
// Base class for declaring an assert hook
// Each of the base class functions can be overridden
// or used by derived classes.
namespace Fw {
// Base class for declaring an assert hook
class AssertHook {
public:
AssertHook() : previousHook(nullptr) {}; //!< constructor
virtual ~AssertHook() {}; //!< destructor
// override this function to intercept asserts
virtual void reportAssert(FILE_NAME_ARG file,
FwSizeType lineNo,
FwSizeType numArgs,
FwAssertArgType arg1,
FwAssertArgType arg2,
FwAssertArgType arg3,
FwAssertArgType arg4,
FwAssertArgType arg5,
FwAssertArgType arg6);
// default reportAssert() will call this when the message is built
// override it to do another kind of print. printf by default
virtual void printAssert(const CHAR* msg);
// do assert action. By default, calls assert.
// Called after reportAssert()
virtual void doAssert();
// register the hook
void registerHook();
// deregister the hook
void deregisterHook();
// get a pointer to the registered hook
static AssertHook* getRegisteredHook();
protected:
private:
// the currently registered assert hook
static AssertHook* s_assertHook;
// the previous assert hook
AssertHook* previousHook;
};
} // namespace Fw
#endif // FW_ASSERT_HPP