-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathassert.hpp
More file actions
211 lines (166 loc) · 6.69 KB
/
Copy pathassert.hpp
File metadata and controls
211 lines (166 loc) · 6.69 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
208
209
210
211
// Copyright 2022-2025 UnoDB contributors
#ifndef UNODB_DETAIL_ASSERT_HPP
#define UNODB_DETAIL_ASSERT_HPP
/// \file
/// Internal macros for assertions, assumptions & intentional crashing.
///
/// If compiling as a part of another project, they will expand to C++ standard
/// symbols (`assert` & `std::abort`). Otherwise, custom implementations are
/// used that will show stacktraces if Boost.Stacktrace is available.
/// \ingroup internal
/// \addtogroup internal
/// \{
// Macros that have multiple definitions are documented once.
/// \name Assertion & assumption macros
/// \{
/// \def UNODB_DETAIL_ASSERT(condition)
/// \hideinitializer
/// Assert a condition.
///
/// Should be used everywhere instead of the standard `assert` macro and will
/// expand to it if building as a part of another project, thus using its
/// replaced declaration, if any. If building standalone, will print a
/// stacktrace on failures if Boost.Stacktrace is available.
/// \def UNODB_DETAIL_CRASH
/// \hideinitializer
/// Intentionally crash.
/// \def UNODB_DETAIL_DEBUG_CRASH()
/// \hideinitializer
/// Crash with a stacktrace on debug build, do nothing on release build.
#include <cstdlib>
#include <iostream>
#include <sstream>
#include <string_view>
#include <thread>
#ifndef UNODB_DETAIL_STANDALONE
#include <cassert>
#endif
#ifdef UNODB_DETAIL_BOOST_STACKTRACE
#if defined(__linux__) && !defined(__clang__)
#define BOOST_STACKTRACE_USE_BACKTRACE
#elif defined(__APPLE__)
#define BOOST_STACKTRACE_GNU_SOURCE_NOT_REQUIRED
#endif
#include <boost/stacktrace.hpp>
#endif
#include "test_heap.hpp"
namespace unodb::detail {
// LCOV_EXCL_START
UNODB_DETAIL_DISABLE_MSVC_WARNING(26447)
/// Print a message and a stacktrace to std::cerr, then abort.
[[noreturn, gnu::cold]] UNODB_DETAIL_HEADER_NOINLINE void msg_stacktrace_abort(
std::string_view msg) noexcept {
UNODB_DETAIL_FAIL_ON_NTH_ALLOCATION(0);
std::ostringstream buf;
buf << msg;
#ifdef UNODB_DETAIL_BOOST_STACKTRACE
buf << boost::stacktrace::stacktrace();
#else
buf << "(stacktrace not available, not compiled with Boost.Stacktrace)\n";
#endif
std::cerr << buf.str();
std::abort();
}
// Definitions that only depend on Debug vs Release
#ifndef NDEBUG
#define UNODB_DETAIL_DEBUG_CRASH() UNODB_DETAIL_CRASH()
/// Implementation for marking a source code location as unreachable.
///
/// Should not be called directly - use UNODB_DETAIL_CANNOT_HAPPEN instead.
[[noreturn, gnu::cold]] UNODB_DETAIL_C_STRING_ARG(1)
UNODB_DETAIL_C_STRING_ARG(3) UNODB_DETAIL_HEADER_NOINLINE void
cannot_happen(const char* file, int line, const char* func) noexcept {
unodb::test::allocation_failure_injector::fail_on_nth_allocation(0);
std::ostringstream buf;
buf << "Execution reached an unreachable point at " << file << ':' << line
<< ": function \"" << func << "\", thread " << std::this_thread::get_id()
<< '\n';
msg_stacktrace_abort(buf.str());
}
#else // !NDEBUG
#define UNODB_DETAIL_DEBUG_CRASH()
[[noreturn, gnu::cold]] UNODB_DETAIL_C_STRING_ARG(1)
UNODB_DETAIL_C_STRING_ARG(3)
UNODB_DETAIL_HEADER_NOINLINE void cannot_happen(const char*, int,
const char*) noexcept {
UNODB_DETAIL_UNREACHABLE();
}
#endif // !NDEBUG
// Definitions that only depend on standalone vs part of another project
#ifdef UNODB_DETAIL_STANDALONE
/// Intentionally crash from a given source location.
///
/// Should not be called directly - use UNODB_DETAIL_CRASH instead.
[[noreturn, gnu::cold]] UNODB_DETAIL_C_STRING_ARG(1)
UNODB_DETAIL_C_STRING_ARG(3) UNODB_DETAIL_HEADER_NOINLINE void
crash(const char* file, int line, const char* func) noexcept {
UNODB_DETAIL_FAIL_ON_NTH_ALLOCATION(0);
std::ostringstream buf;
buf << "Crash requested at " << file << ':' << line << ", function \"" << func
<< "\", thread " << std::this_thread::get_id() << '\n';
msg_stacktrace_abort(buf.str());
}
#define UNODB_DETAIL_CRASH() unodb::detail::crash(__FILE__, __LINE__, __func__)
#else // UNODB_DETAIL_STANDALONE
#define UNODB_DETAIL_CRASH() std::abort()
#endif // UNODB_DETAIL_STANDALONE
// Definitions that depend on both Debug vs Release and standalone vs part of
// another project
#ifndef UNODB_DETAIL_STANDALONE
#define UNODB_DETAIL_ASSERT(condition) assert(condition)
#elif !defined(NDEBUG)
/// Assert failure implementation for standalone debug build.
///
/// Should not be called directly - used UNODB_DETAIL_ASSERT instead.
[[noreturn, gnu::cold]] UNODB_DETAIL_C_STRING_ARG(1)
UNODB_DETAIL_C_STRING_ARG(3) UNODB_DETAIL_C_STRING_ARG(4)
UNODB_DETAIL_HEADER_NOINLINE void
assert_failure(const char* file, int line, const char* func,
const char* condition) noexcept {
unodb::test::allocation_failure_injector::fail_on_nth_allocation(0);
std::ostringstream buf;
buf << "Assertion \"" << condition << "\" failed at " << file << ':' << line
<< ", function \"" << func << "\", thread " << std::this_thread::get_id()
<< '\n';
msg_stacktrace_abort(buf.str());
}
#define UNODB_DETAIL_ASSERT(condition) \
UNODB_DETAIL_UNLIKELY(!(condition)) \
? unodb::detail::assert_failure(__FILE__, __LINE__, __func__, #condition) \
: ((void)0)
#else // !defined(NDEBUG)
#define UNODB_DETAIL_ASSERT(condition) ((void)0)
#endif // !defined(NDEBUG)
/// Assert that is only active when QSBR debug checking is enabled.
/// Use for assertions that reference QSBR state (qsbr::instance(), etc.)
/// which may not be available in builds without QSBR linked.
#ifdef UNODB_DETAIL_QSBR_DEBUG
#define UNODB_DETAIL_QSBR_ASSERT(condition) UNODB_DETAIL_ASSERT(condition)
#else
#define UNODB_DETAIL_QSBR_ASSERT(condition) ((void)0)
#endif
UNODB_DETAIL_RESTORE_MSVC_WARNINGS()
} // namespace unodb::detail
/// Provide an assumption for the compiler.
///
/// The assumption is expressed as a condition that always holds, i.e. an
/// allowed value range for a variable, which may allow the compiler to e.g.
/// optimize a redundant check away or silence a warning. Plain assertions
/// should be used almost always instead, and replaced with assumptions only
/// with provable effect on the diagnostics or generated code.
#define UNODB_DETAIL_ASSUME(assumption) \
do { \
UNODB_DETAIL_ASSERT(assumption); \
UNODB_DETAIL_BUILTIN_ASSUME(assumption); \
} while (0)
/// Mark this source code location as unreachable.
///
/// Under release build the location is annotated for the compiler as
/// unreachable, potentially enabling more optimizations. Under debug build, if
/// execution comes here, it will crash with a stacktrace.
#define UNODB_DETAIL_CANNOT_HAPPEN() \
unodb::detail::cannot_happen(__FILE__, __LINE__, __func__)
// LCOV_EXCL_STOP
/// \}
/// \}
#endif