svMultiPhysics
Loading...
Searching...
No Matches
Exception.h
1// SPDX-FileCopyrightText: Copyright (c) Stanford University, The Regents of the University of California, and others.
2// SPDX-License-Identifier: BSD-3-Clause
3
4#ifndef SVMP_CORE_EXCEPTION_H
5#define SVMP_CORE_EXCEPTION_H
6
7#include <cstdint>
8#include <cstdlib>
9#include <exception>
10#include <iostream>
11#include <sstream>
12#include <string>
13#include <string_view>
14#include <utility>
15#include <vector>
16
17#if !defined(NDEBUG) || defined(DEBUG) || defined(_DEBUG)
18# define SVMP_EXCEPTION_DEBUG_MODE 1
19#else
20# define SVMP_EXCEPTION_DEBUG_MODE 0
21#endif
22
23namespace svmp {
24
25enum class StatusCode : std::uint8_t {
26 Success = 0,
27 InvalidArgument,
28 InvalidState,
29 ParseError,
30 IOError,
31 ResourceExhausted,
32 DependencyError,
33 MPIError,
34 NotImplemented,
35 UnsupportedOperation,
36 InternalError,
37 Unknown = 255
38};
39
40inline const char* status_code_to_string(StatusCode status) noexcept
41{
42 switch (status) {
43 case StatusCode::Success:
44 return "Success";
45 case StatusCode::InvalidArgument:
46 return "Invalid argument";
47 case StatusCode::InvalidState:
48 return "Invalid state";
49 case StatusCode::ParseError:
50 return "Parse error";
51 case StatusCode::IOError:
52 return "I/O error";
53 case StatusCode::ResourceExhausted:
54 return "Resource exhausted";
55 case StatusCode::DependencyError:
56 return "Dependency error";
57 case StatusCode::MPIError:
58 return "MPI error";
59 case StatusCode::NotImplemented:
60 return "Not implemented";
61 case StatusCode::UnsupportedOperation:
62 return "Unsupported operation";
63 case StatusCode::InternalError:
64 return "Internal error";
65 default:
66 return "Unknown error";
67 }
68}
69
71 const char* file;
72 int line;
73 const char* function;
74};
75
76class StackTraceFrame final {
77public:
78 StackTraceFrame() = default;
79
80 StackTraceFrame(std::string symbol, std::string module, std::string file,
81 int line, std::uintptr_t address) noexcept
82 : symbol_(std::move(symbol)),
83 module_(std::move(module)),
84 file_(std::move(file)),
85 line_(line),
86 address_(address)
87 {
88 }
89
90 const std::string& symbol() const noexcept { return symbol_; }
91 const std::string& module() const noexcept { return module_; }
92 const std::string& file() const noexcept { return file_; }
93 int line() const noexcept { return line_; }
94 std::uintptr_t address() const noexcept { return address_; }
95
96private:
97 std::string symbol_;
98 std::string module_;
99 std::string file_;
100 int line_ = 0;
101 std::uintptr_t address_ = 0;
102};
103
104class StackTrace final {
105public:
106 bool empty() const noexcept { return frames_.empty(); }
107 std::size_t size() const noexcept { return frames_.size(); }
108 const std::vector<StackTraceFrame>& frames() const noexcept { return frames_; }
109 void add_frame(StackTraceFrame frame) { frames_.push_back(std::move(frame)); }
110
111private:
112 std::vector<StackTraceFrame> frames_;
113};
114
116 bool has_stack_trace;
117 bool has_symbol_resolution;
118 bool has_demangling;
119};
120
121class PlatformSupport final {
122public:
123 static PlatformCapabilities capabilities() noexcept;
124 static int query_mpi_rank() noexcept;
125 static StackTrace capture_stack_trace();
126 static std::string demangle_symbol(const char* symbol);
127 static void finalize_mpi_if_needed() noexcept;
128 static void abort_mpi_if_needed(int exit_code) noexcept;
129};
130} // namespace svmp
131
132#define SVMP_CORE_EXCEPTION_INCLUDE_PLATFORM_SUPPORT
133#include "PlatformSupport.inl"
134#undef SVMP_CORE_EXCEPTION_INCLUDE_PLATFORM_SUPPORT
135
136namespace svmp {
137
138class ExceptionContext final {
139public:
140 StatusCode status_code() const noexcept { return status_code_; }
141 const std::string& file() const noexcept { return file_; }
142 int line() const noexcept { return line_; }
143 const std::string& function() const noexcept { return function_; }
144 int mpi_rank() const noexcept { return mpi_rank_; }
145 const StackTrace& stack_trace() const noexcept { return stack_trace_; }
146
147 void set_status_code(StatusCode status_code) noexcept
148 {
149 status_code_ = status_code;
150 }
151
152 void set_source_location(const char* file, int line, const char* function)
153 {
154 file_ = (file == nullptr) ? std::string() : std::string(file);
155 line_ = line;
156 function_ =
157 (function == nullptr) ? std::string() : std::string(function);
158 }
159
160 void set_mpi_rank(int mpi_rank) noexcept { mpi_rank_ = mpi_rank; }
161 void set_stack_trace(StackTrace stack_trace)
162 {
163 stack_trace_ = std::move(stack_trace);
164 }
165
166private:
167 StatusCode status_code_ = StatusCode::Unknown;
168 std::string file_;
169 int line_ = 0;
170 std::string function_;
171 int mpi_rank_ = -1;
172 StackTrace stack_trace_;
173};
174
175namespace ExceptionFormatter {
176
177inline std::string format(const ExceptionContext& context,
178 const std::string& message,
179 std::string_view subsystem_label = "Exception")
180{
181 if (subsystem_label.empty()) {
182 subsystem_label = "Exception";
183 }
184
185 std::ostringstream oss;
186
187 oss << "[" << subsystem_label << "] "
188 << status_code_to_string(context.status_code());
189 if (context.mpi_rank() >= 0) {
190 oss << " (Rank " << context.mpi_rank() << ")";
191 }
192 oss << "\n";
193
194 if (!context.file().empty()) {
195 oss << " Location: " << context.file() << ":" << context.line();
196 if (!context.function().empty()) {
197 oss << " in " << context.function() << "()";
198 }
199 oss << "\n";
200 }
201
202 oss << " Message: " << message << "\n";
203
204 if (!context.stack_trace().empty()) {
205 oss << " Stack trace:\n";
206 std::size_t frame_index = 0;
207 for (const auto& frame : context.stack_trace().frames()) {
208 oss << " #" << frame_index++ << " ";
209 if (!frame.symbol().empty()) {
210 oss << frame.symbol();
211 } else {
212 std::ostringstream address;
213 address << "0x" << std::hex << frame.address();
214 oss << address.str();
215 }
216
217 if (!frame.module().empty()) {
218 oss << " [" << frame.module() << "]";
219 }
220
221 if (!frame.file().empty()) {
222 oss << " (" << frame.file();
223 if (frame.line() > 0) {
224 oss << ":" << frame.line();
225 }
226 oss << ")";
227 }
228
229 oss << "\n";
230 }
231 }
232
233 return oss.str();
234}
235
236} // namespace ExceptionFormatter
237
238class ExceptionBase;
239
240namespace ExceptionRuntime {
241
242 inline int query_mpi_rank() noexcept
243 {
244 return PlatformSupport::query_mpi_rank();
245 }
246
247 inline StackTrace capture_stack_trace()
248 {
249 return PlatformSupport::capture_stack_trace();
250 }
251
252 inline void finalize_mpi_if_needed() noexcept
253 {
254 PlatformSupport::finalize_mpi_if_needed();
255 }
256
257 void install_terminate_handler();
258
259 inline void report_unhandled_exception(const std::exception& exception)
260 {
261 std::cerr << exception.what() << std::endl;
262 }
263
264 inline void abort_mpi_if_needed(int exit_code) noexcept
265 {
266 PlatformSupport::abort_mpi_if_needed(exit_code);
267 }
268
269} // namespace ExceptionRuntime
270
271class ExceptionBase : public std::exception {
272public:
273 const char* what() const noexcept override { return what_.c_str(); }
274 StatusCode status_code() const noexcept { return context_.status_code(); }
275 const std::string& message() const noexcept { return message_; }
276 const ExceptionContext& context() const noexcept { return context_; }
277
278 void add_context(const std::string& context)
279 {
280 message_ = context + "\n -> " + message_;
281 rebuild_what();
282 }
283
284 virtual ~ExceptionBase() noexcept = default;
285
286protected:
287 ExceptionBase(std::string message, StatusCode status,
288 std::string_view subsystem_label, const char* file,
289 int line, const char* function)
290 : message_(std::move(message)),
291 subsystem_label_(subsystem_label.empty() ? std::string_view("Exception")
292 : subsystem_label)
293 {
294 context_.set_status_code(status);
295 context_.set_source_location(file, line, function);
296 context_.set_mpi_rank(ExceptionRuntime::query_mpi_rank());
297#if SVMP_EXCEPTION_DEBUG_MODE
298 context_.set_stack_trace(ExceptionRuntime::capture_stack_trace());
299#endif
300 rebuild_what();
301 }
302
303 void rebuild_what()
304 {
305 what_ = ExceptionFormatter::format(context_, message_, subsystem_label_);
306 }
307
308 std::string message_;
309 ExceptionContext context_;
310 std::string_view subsystem_label_;
311 std::string what_;
312};
313
315public:
316 CoreException(const std::string& message,
317 StatusCode status = StatusCode::Unknown,
318 const char* file = "",
319 int line = 0,
320 const char* function = "")
321 : ExceptionBase(message, status, "Core Exception", file, line, function)
322 {
323 }
324};
325
327public:
328 ParseException(const std::string& message,
329 const char* file = "",
330 int line = 0,
331 const char* function = "")
332 : CoreException(message, StatusCode::ParseError, file, line, function)
333 {
334 }
335};
336
338public:
339 DependencyException(const std::string& message,
340 const char* file = "",
341 int line = 0,
342 const char* function = "")
343 : CoreException(message, StatusCode::DependencyError, file, line,
344 function)
345 {
346 }
347};
348
349inline void ExceptionRuntime::install_terminate_handler()
350{
351 std::set_terminate([]() {
352 try {
353 const std::exception_ptr current = std::current_exception();
354 if (current != nullptr) {
355 std::rethrow_exception(current);
356 }
357 } catch (const std::exception& exception) {
358 ExceptionRuntime::report_unhandled_exception(exception);
359 } catch (...) {
360 std::cerr << "[Unhandled Exception] Unknown non-std exception"
361 << std::endl;
362 }
363
364 ExceptionRuntime::abort_mpi_if_needed(EXIT_FAILURE);
365 std::abort();
366 });
367}
368
369template <class ExceptionT, class... Args>
370[[noreturn]] void raise(SourceLocation location, Args&&... args)
371{
372 throw ExceptionT(std::forward<Args>(args)..., location.file, location.line,
373 location.function);
374}
375
376template <class ExceptionT, class... Args>
377void check(bool condition, SourceLocation location, Args&&... args)
378{
379 if (!condition) {
380 raise<ExceptionT>(location, std::forward<Args>(args)...);
381 }
382}
383
384template <class ExceptionT, class... Args>
385void check_arg(bool condition, SourceLocation location, Args&&... args)
386{
387 if (!condition) {
388 raise<ExceptionT>(location, std::forward<Args>(args)...);
389 }
390}
391
392template <class ExceptionT, class PointerT, class... Args>
393void check_not_null(PointerT ptr, SourceLocation location, Args&&... args)
394{
395 if (ptr == nullptr) {
396 raise<ExceptionT>(location, std::forward<Args>(args)...);
397 }
398}
399
400} // namespace svmp
401
402#define SVMP_HERE ::svmp::SourceLocation{__FILE__, __LINE__, __func__}
403
404#if SVMP_EXCEPTION_DEBUG_MODE
405#define SVMP_DEBUG_CHECK(ExceptionT, condition, ...) \
406 do { \
407 if (!(condition)) { \
408 ::svmp::raise<ExceptionT>(SVMP_HERE, __VA_ARGS__); \
409 } \
410 } while (false)
411#else
412#define SVMP_DEBUG_CHECK(ExceptionT, condition, ...) \
413 do { \
414 } while (false)
415#endif
416
417#endif // SVMP_CORE_EXCEPTION_H
Definition Exception.h:314
Definition Exception.h:337
Definition Exception.h:271
Definition Exception.h:138
Definition Exception.h:326
Definition Exception.h:121
Definition Exception.h:76
Definition Exception.h:104
Definition Exception.h:115
Definition Exception.h:70