DNDSR 0.2.1
Distributed Numeric Data Structure for CFV
Loading...
Searching...
No Matches
Errors.hpp
Go to the documentation of this file.
1#pragma once
2/// @file Errors.hpp
3/// @brief Assertion / error-handling macros and supporting helper functions.
4///
5/// ## Overview
6/// Three distinct families of checks are provided; choose based on how the
7/// failure should surface:
8///
9/// | Macro | Release behaviour | Failure mode |
10/// |------------------------|-------------------------|---------------------------|
11/// | @ref DNDS_assert | Compiled out (NDEBUG) | `std::abort()` |
12/// | @ref DNDS_assert_info | Compiled out (NDEBUG) | `std::abort()` + message |
13/// | @ref DNDS_assert_infof | Compiled out (NDEBUG) | `std::abort()` + fmtprintf|
14/// | @ref DNDS_check_throw | Always active | `throw std::runtime_error`|
15/// | @ref DNDS_check_throw_info | Always active | `throw` + message |
16/// | @ref DNDS_HD_assert | Compiled out in NDEBUG | host: `abort`, device: `trap` |
17///
18/// Prefer @ref DNDS_assert for internal invariants that are expensive to check or
19/// cannot fail in correct code; use @ref DNDS_check_throw for user-input / runtime
20/// validation that must remain active in release builds.
21///
22/// The device variants (`DNDS_HD_*`) expand to host asserts on the host and to
23/// atomic-guarded PTX `trap` on CUDA devices so only one thread prints.
24
25#include "Macros.hpp"
26
27// assert macros
28
29#include <iostream>
30#include <cstdarg>
31#include <array>
32#include <sstream>
33namespace DNDS
34{
35 /// @brief Return a symbolicated stack trace for the calling thread.
36 /// @details Host-only, implemented with `boost::stacktrace` (or similar).
37 /// Used by the `assert_false*` helpers below.
38 std::string getTraceString();
39
40 /// @brief Low-level: print a red "DNDS_assertion failed" line and abort.
41 inline void assert_false(const char *expr, const char *file, int line)
42 {
43 std::cerr << getTraceString() << "\n";
44 std::cerr << "\033[91m DNDS_assertion failed\033[39m: \"" << expr << "\" at [ " << file << ":" << line << " ]" << std::endl;
45 std::abort();
46 }
47
48 /// @brief Variant of #assert_false that prints an extra `info` string.
49 inline void assert_false_info(const char *expr, const char *file, int line, const std::string &info)
50 {
51 std::cerr << getTraceString() << "\n";
52 std::cerr << "\033[91m DNDS_assertion failed\033[39m: \"" << expr << "\" at [ " << file << ":" << line << " ]\n"
53 << info << std::endl;
54 std::abort();
55 }
56
57 /// @brief `printf`-style variant of #assert_false. Used by @ref DNDS_assert_infof.
58 inline void assert_false_infof(const char *expr, const char *file, int line,
59 const char *info, ...)
60 {
61 va_list args;
62 va_start(args, info);
63 std::cerr << getTraceString() << "\n";
64 std::cerr << "\033[91m DNDS_assertion failed\033[39m: \"" << expr << "\" at [ " << file << ":" << line << " ]\n";
65 // Compile-time constant 1024 * 512 = 524288 fits in int32_t;
66 // no runtime overflow is possible.
67 // NOLINTNEXTLINE(bugprone-implicit-widening-of-multiplication-result)
68 std::array<char, 1024 * 512> format_buf{};
69 std::vsnprintf(format_buf.data(), format_buf.size(), info, args);
70 va_end(args);
71 std::cerr << format_buf.data() << std::endl;
72 std::abort();
73 }
74
75 /// @brief Throwing variant of #assert_false_info. Used by @ref DNDS_check_throw.
76 /// @tparam TException Exception type to throw (defaults to `std::runtime_error`).
77 /// Currently the implementation ignores the template parameter and always
78 /// throws `std::runtime_error`; kept for future customisation.
79 template <class TException = std::runtime_error>
80 void assert_false_info_throw(const char *expr, const char *file, int line, const std::string &info)
81 {
82 std::stringstream ss;
83 ss << getTraceString() << "\n";
84 ss << "\033[91m DNDS_assertion failed\033[39m: \"" << expr << "\" at [ " << file << ":" << line << " ]\n"
85 << info << std::endl;
86 throw std::runtime_error(ss.str());
87 }
88}
89
90/// @brief Runtime check active in both debug and release builds.
91/// Throws `std::runtime_error` if `expr` evaluates to `false`.
92/// Prefer this over @ref DNDS_assert for user-input and API-contract checks.
93#define DNDS_check_throw(expr) \
94 ((static_cast<bool>(expr)) \
95 ? void(0) \
96 : ::DNDS::assert_false_info_throw(#expr, __FILE__, __LINE__, ""))
97
98/// @brief Same as @ref DNDS_check_throw but attaches a user-supplied `info` message
99/// to the thrown `std::runtime_error`.
100#define DNDS_check_throw_info(expr, info) \
101 ((static_cast<bool>(expr)) \
102 ? void(0) \
103 : ::DNDS::assert_false_info_throw(#expr, __FILE__, __LINE__, info))
104
105#ifdef DNDS_NDEBUG
106# define DNDS_assert(expr) (void(0))
107# define DNDS_assert_info(expr, info) (void(0))
108# define DNDS_assert_infof(expr, info, ...) (void(0))
109#else
110/// @brief Debug-only assertion (compiled out when @ref DNDS_NDEBUG is defined).
111/// Prints the expression + file/line + backtrace, then calls `std::abort()`.
112# define DNDS_assert(expr) \
113 ((static_cast<bool>(expr)) \
114 ? void(0) \
115 : ::DNDS::assert_false(#expr, __FILE__, __LINE__))
116/// @brief Debug-only assertion with an extra std::string `info` message.
117# define DNDS_assert_info(expr, info) \
118 ((static_cast<bool>(expr)) \
119 ? void(0) \
120 : ::DNDS::assert_false_info(#expr, __FILE__, __LINE__, info))
121/// @brief Debug-only assertion with a printf-style format message.
122# define DNDS_assert_infof(expr, info, ...) \
123 ((static_cast<bool>(expr)) \
124 ? void(0) \
125 : ::DNDS::assert_false_infof(#expr, __FILE__, __LINE__, info, ##__VA_ARGS__))
126#endif
127
128#ifdef __CUDA_ARCH__
129
130/// @brief Device-side assertion failure: print once (atomic-guarded) and trap.
131/// @details Uses `atomicCAS` on a managed flag so only the first failing thread
132/// prints; all other threads simply call `asm("trap;")`. Avoids flooding the
133/// console when a kernel has one bug hit by thousands of threads.
134__device__ inline void device_assert_fail(const char *expr, const char *file, int line)
135{
136 __device__ __managed__ static int g_assert_printed = 0;
137 if (atomicCAS(&g_assert_printed, 0, 1) == 0)
138 {
139 printf("Device assert failed: %s at %s:%d (block %d thread %d)\n",
140 expr, file, line, blockIdx.x, threadIdx.x);
141 asm("trap;"); // force termination
142 }
143}
144
145/// @brief Printf-formatted variant of #device_assert_fail.
146__device__ inline void device_assert_fail_infof(const char *expr, const char *file, int line,
147 char *info, ...)
148{
149 __device__ __managed__ static int g_assert_printed = 0;
150 if (atomicCAS(&g_assert_printed, 0, 1) == 0)
151 {
152 va_list args;
153 va_start(args, info);
154 printf("Device assert failed: %s at %s:%d (block %d thread %d)\n",
155 expr, file, line, blockIdx.x, threadIdx.x);
156 vprintf(info, args);
157 va_end(args);
158 asm("trap;"); // force termination
159 }
160}
161
162# if defined(DNDS_NDEBUG) || defined(DNDS_NDEBUG_DEVICE)
163# define DNDS_HD_assert(cond) (void(0))
164# define DNDS_HD_assert_infof(cond, info, ...) (void(0))
165# else
166/// @brief Host/device assertion: abort on host, PTX `trap` on CUDA device.
167/// @details Can be used inside `__host__ __device__` functions. Disabled when
168/// either @ref DNDS_NDEBUG (host+device) or @ref DNDS_NDEBUG_DEVICE (device-only) is set.
169# define DNDS_HD_assert(cond) \
170 do \
171 { \
172 if (!(cond)) \
173 { \
174 device_assert_fail(#cond, __FILE__, __LINE__); \
175 } \
176 } while (0)
177
178/// @brief Host/device assertion with a printf-format message.
179# define DNDS_HD_assert_infof(cond, info, ...) \
180 do \
181 { \
182 if (!(cond)) \
183 { \
184 device_assert_fail(#cond, __FILE__, __LINE__); \
185 } \
186 } while (0)
187
188# endif
189#else
190
191// HOST version
192/// @brief Host-only expansion of @ref DNDS_HD_assert (equivalent to @ref DNDS_assert).
193# define DNDS_HD_assert(cond) DNDS_assert(cond)
194/// @brief Host-only expansion of @ref DNDS_HD_assert_infof.
195# define DNDS_HD_assert_infof(cond, info, ...) DNDS_assert_infof(cond, info, ##__VA_ARGS__)
196#endif
197
198#ifdef __CUDA_ARCH__
199# ifdef DNDS_DEVICE_BAN_EIGEN_MALLOC_DYNAMIC
200# define EIGEN_RUNTIME_NO_MALLOC
201# else
202# define EIGEN_NO_MALLOC
203# endif
204# define eigen_assert(expr) DNDS_HD_assert(expr) //! we overwrite the eigen's assert
205# if defined(EIGEN_RUNTIME_NO_MALLOC) && !defined(EIGEN_NO_MALLOC)
206# define DNDS_DEVICE_CODE_GUARD_EIGEN_MALLOC (Eigen::internal::set_is_malloc_allowed(false))
207# endif
208
209#else
210
211# define DNDS_DEVICE_CODE_GUARD_EIGEN_MALLOC (void(0))
212
213#endif
214
215namespace DNDS
216{
217}
Project-wide preprocessor flags, branch-hint macros, and version/build metadata strings....
the host side operators are provided as implemented
void assert_false(const char *expr, const char *file, int line)
Low-level: print a red "DNDS_assertion failed" line and abort.
Definition Errors.hpp:41
void assert_false_infof(const char *expr, const char *file, int line, const char *info,...)
printf-style variant of assert_false. Used by DNDS_assert_infof.
Definition Errors.hpp:58
void assert_false_info_throw(const char *expr, const char *file, int line, const std::string &info)
Throwing variant of assert_false_info. Used by DNDS_check_throw.
Definition Errors.hpp:80
void assert_false_info(const char *expr, const char *file, int line, const std::string &info)
Variant of assert_false that prints an extra info string.
Definition Errors.hpp:49
std::string getTraceString()
Return a symbolicated stack trace for the calling thread.
Definition Defines.cpp:165