SuperDex Physics C++ API
Loading...
Searching...
No Matches
debug_inl.h
Go to the documentation of this file.
1/*
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#pragma once
18#include "debug.h" // Reverse include for Intellisense
19
20#include <mutex>
21#include <type_traits>
22
23namespace superdex {
24
25#if MOCHI_ARCH_CPU
26// Assert reporting in CPU code
27#if MOCHI_HAS_VA_OPT
28#define MOCHI_ASSERT_ON_FAILURE(file, line, conditionStr, ...) \
29 if (::superdex::OnAssertionFailure(file, line, conditionStr __VA_OPT__(, )##__VA_ARGS__)) { \
30 MOCHI_DEBUG_BREAK(); \
31 }
32#else
33#define MOCHI_ASSERT_ON_FAILURE(file, line, conditionStr, ...) \
34 if (::superdex::OnAssertionFailure(file, line, conditionStr, ##__VA_ARGS__)) { \
35 MOCHI_DEBUG_BREAK(); \
36 }
37#endif
38#elif MOCHI_ARCH_GPU
39// GPU paths cannot use the full logger functionality. Instead we have to rely on simpler
40// mechanisms to get the point across. CUDA for example, supports printf.
41//
42// @TODO[Nate,Hector] Is ther an alternative for MOCHI_DEBUG_BREAK in CUDA code? The compiler
43// currently reports errors any time it is used inside a constexpr function (of which we have many).
44#define MOCHI_ASSERT_ON_FAILURE(file, line, conditionStr, ...) \
45 printf("%s(%d): Assertion Failure! Expected (%s)\n", file, line, conditionStr); \
46 printf("" __VA_ARGS__); \
47 printf("\n"); \
48 /*MOCHI_DEBUG_BREAK()*/
49#else
50#error Expected MOCHI_ARCH_CPU or MOCHI_ARCH_GPU
51#define MOCHI_ASSERT_ON_FAILURE
52#endif
53
54// Assert macro implementation (does not compile out if used directly)
55#if MOCHI_HAS_VA_OPT
56#define MOCHI_ASSERT_IMPL(condition, ...) \
57 if (condition) { \
58 } else \
59 MOCHI_UNLIKELY { \
60 MOCHI_ASSERT_ON_FAILURE(__FILE__, __LINE__, #condition __VA_OPT__(, )##__VA_ARGS__); \
61 }
62#else
63#define MOCHI_ASSERT_IMPL(condition, ...) \
64 if (condition) { \
65 } else \
66 MOCHI_UNLIKELY { \
67 MOCHI_ASSERT_ON_FAILURE(__FILE__, __LINE__, #condition, ##__VA_ARGS__); \
68 }
69#endif
70
71// These implementation functions are inlined so that MOCHI_ASSERT can be used
72// via header-only dependency. This does not require linking with MochiCore.
73namespace assert_impl {
74
75#if MOCHI_ARCH_CPU
76inline std::recursive_mutex& GetAssertMutexRef() {
77 static std::recursive_mutex s_assertMutex;
78 return s_assertMutex;
79}
80inline OnAssertFn& GetOnAssertFnRef() {
81 static OnAssertFn s_onAssertFn;
82 return s_onAssertFn;
83}
84inline int& GetAssertCallDepthRef() {
85 static int s_assertCallDepth = 0;
86 return s_assertCallDepth;
87}
88#endif // MOCHI_ARCH_CPU
89
90inline static bool
91DefaultAssertHandler(char const* condition, char const* message, char const* file, int line) {
93 "\n"
94 "*****************************************************************************\n"
95 "MOCHI ASSERTION FAILURE:\n"
96 " Message: %s\n"
97 " Expected: (%s)\n"
98 " Location: %s(%d)\n"
99 "*****************************************************************************\n",
100 message,
101 condition,
102 file,
103 line);
104
105 // Make sure the message is flushed before the breakpoint
106 FlushLog();
107
108 // TODO:
109 // Show a dialog box with options like: "Abort", "Debug", and "Continue at your own risk".
110 // For now, we will always stop at a breakpoint, or crash with a non-zero exit code if no debugger
111 // is connected.
112 bool const triggerDebugBreak = true;
113
114 return triggerDebugBreak;
115}
116
117} // namespace assert_impl
118
119template <class DstPtrT, class SrcT>
120MOCHI_FORCE_INLINE DstPtrT assert_cast(SrcT* ptr) {
121 static_assert(std::is_pointer_v<DstPtrT>, "Invalid cast. Expected a pointer type.");
122 MOCHI_ASSERT(dynamic_cast<DstPtrT>(ptr) == static_cast<DstPtrT>(ptr), "Invalid cast");
123 return static_cast<DstPtrT>(ptr);
124}
125template <class DstRefT, class SrcT>
126MOCHI_FORCE_INLINE DstRefT assert_cast(SrcT&& ref) {
127 static_assert(std::is_reference_v<DstRefT>, "Invalid cast. Expected a reference type.");
129 dynamic_cast<std::remove_reference_t<DstRefT>*>(&ref) ==
130 static_cast<std::remove_reference_t<DstRefT>*>(&ref),
131 "Invalid cast");
132 return static_cast<DstRefT>(ref);
133}
134
135/** @cond */
136
137inline OnAssertFn GetAssertionFailureCallback() {
138#if MOCHI_ARCH_CPU
139 std::lock_guard<std::recursive_mutex> lock(assert_impl::GetAssertMutexRef());
140 auto& fn = assert_impl::GetOnAssertFnRef();
141 return fn ? fn : OnAssertFn{assert_impl::DefaultAssertHandler};
142#else
143 return OnAssertFn{assert_impl::DefaultAssertHandler};
144#endif
145}
146
147inline void SetAssertionFailureCallback([[maybe_unused]] OnAssertFn fn) {
148#if MOCHI_ARCH_CPU
149 std::lock_guard<std::recursive_mutex> lock(assert_impl::GetAssertMutexRef());
150 assert_impl::GetOnAssertFnRef() = fn ? fn : OnAssertFn{assert_impl::DefaultAssertHandler};
151#endif
152}
153
154// Overload for asserts with WITHOUT formatted args. Never inline this function!
155MOCHI_NO_INLINE inline bool
156OnAssertionFailure(char const* file, int line, char const* condition, char const* msg) {
157#if MOCHI_ARCH_CPU
158 std::lock_guard<std::recursive_mutex> lock(assert_impl::GetAssertMutexRef());
159 int& callDepth = assert_impl::GetAssertCallDepthRef();
160 if (callDepth != 0) {
161 // HELP! Assert failure within an assert handler function!
163 }
164 ++callDepth;
165 bool shouldBreak = GetAssertionFailureCallback()(condition, msg, file, line);
166 --callDepth;
167 return shouldBreak;
168#else
169 return GetAssertionFailureCallback()(condition, msg, file, line);
170#endif
171}
172
173// Overload for asserts with a formatted message. Never inline this function!
174template <typename... Args>
175MOCHI_NO_INLINE bool OnAssertionFailure(
176 char const* file,
177 int line,
178 char const* condition,
179 char const* fmt,
180 Args... args) {
181 return OnAssertionFailure(file, line, condition, Format(fmt, args...).c_str());
182}
183
184// Overload for asserts with no message at all. Never inline this function!
185MOCHI_NO_INLINE inline bool OnAssertionFailure(char const* file, int line, char const* condition) {
186 return OnAssertionFailure(file, line, condition, "");
187}
188
189/** @endcond */
190
191} // namespace superdex
#define MOCHI_ASSERT(condition_without_side_effects,...)
Definition debug.h:81
#define MOCHI_LOG_ERROR(...)
Definition log.h:44
#define MOCHI_DEBUG_BREAK()
#define MOCHI_FORCE_INLINE
std::function< bool(char const *condition, char const *message, char const *file, int line)> OnAssertFn
Callback invoked when an assertion fails.
Definition debug.h:140
void FlushLog()
Definition log.h:157
std::string Format()
Definition log.h:96