1use crate::in_test_configuration;
5use once_cell::sync::Lazy;
6
7#[macro_export]
8macro_rules! fatal {
9 ($msg:literal $(, $arg:expr)*) => {{
10 if $crate::in_antithesis() {
11 let full_msg = format!($msg $(, $arg)*);
12 let json = $crate::logging::json!({ "message": full_msg });
13 $crate::logging::assert_unreachable_antithesis!($msg, &json);
14 }
15 tracing::error!(fatal = true, $msg $(, $arg)*);
16 panic!($msg $(, $arg)*);
17 }};
18}
19
20pub use antithesis_sdk::assert_reachable as assert_reachable_antithesis;
21pub use antithesis_sdk::assert_sometimes as assert_sometimes_antithesis;
22pub use antithesis_sdk::assert_unreachable as assert_unreachable_antithesis;
23
24pub use serde_json::json;
25
26#[inline(always)]
27pub fn crash_on_debug() -> bool {
28 static CRASH_ON_DEBUG: Lazy<bool> = Lazy::new(|| {
29 in_test_configuration() || std::env::var("SUI_ENABLE_DEBUG_ASSERTIONS").is_ok()
30 });
31
32 *CRASH_ON_DEBUG
33}
34
35#[cfg(msim)]
36pub mod intercept_debug_fatal {
37 use std::sync::{Arc, Mutex};
38
39 #[derive(Clone)]
40 pub struct DebugFatalCallback {
41 pub pattern: String,
42 pub callback: Arc<dyn Fn() + Send + Sync>,
43 }
44
45 thread_local! {
46 static INTERCEPT_DEBUG_FATAL: Mutex<Option<DebugFatalCallback>> = Mutex::new(None);
47 }
48
49 pub fn register_callback(message: &str, f: impl Fn() + Send + Sync + 'static) {
50 INTERCEPT_DEBUG_FATAL.with(|m| {
51 *m.lock().unwrap() = Some(DebugFatalCallback {
52 pattern: message.to_string(),
53 callback: Arc::new(f),
54 });
55 });
56 }
57
58 pub fn get_callback() -> Option<DebugFatalCallback> {
59 INTERCEPT_DEBUG_FATAL.with(|m| m.lock().unwrap().clone())
60 }
61}
62
63#[macro_export]
64macro_rules! register_debug_fatal_handler {
65 ($message:literal, $f:expr) => {
66 #[cfg(msim)]
67 $crate::logging::intercept_debug_fatal::register_callback($message, $f);
68
69 #[cfg(not(msim))]
70 {
71 let _ = $f;
73 }
74 };
75}
76
77#[macro_export]
78macro_rules! debug_fatal {
79 ($msg:literal $(, $arg:expr)*) => {{
81 loop {
82 #[cfg(msim)]
83 {
84 if let Some(cb) = $crate::logging::intercept_debug_fatal::get_callback() {
85 tracing::error!($msg $(, $arg)*);
86 let msg = format!($msg $(, $arg)*);
87 if msg.contains(&cb.pattern) {
88 (cb.callback)();
89 }
90 break;
91 }
92 }
93
94 if !$crate::in_antithesis() && $crate::logging::crash_on_debug() {
97 $crate::fatal!($msg $(, $arg)*);
98 } else {
99 let stacktrace = std::backtrace::Backtrace::capture();
100 tracing::error!(debug_fatal = true, stacktrace = ?stacktrace, $msg $(, $arg)*);
101 let location = concat!(file!(), ':', line!());
102 if let Some(metrics) = mysten_metrics::get_metrics() {
103 metrics.system_invariant_violations.with_label_values(&[location]).inc();
104 }
105 if $crate::in_antithesis() {
106 let full_msg = format!($msg $(, $arg)*);
109 let json = $crate::logging::json!({ "message": full_msg });
110 $crate::logging::assert_unreachable_antithesis!($msg, &json);
111 }
112 }
113 break;
114 }
115 }};
116}
117
118#[macro_export]
119macro_rules! assert_reachable {
120 () => {
121 $crate::logging::assert_reachable!("");
122 };
123 ($message:literal) => {{
124 if !cfg!(msim) {
126 $crate::logging::assert_reachable_antithesis!($message);
127 }
128 }};
129}
130
131#[macro_export]
132macro_rules! assert_sometimes {
133 ($expr:expr, $message:literal) => {{
134 if !cfg!(msim) {
136 $crate::logging::assert_sometimes_antithesis!($expr, $message);
137 } else {
138 let _ = $expr;
140 }
141 }};
142}
143
144mod tests {
145 #[test]
146 #[should_panic]
147 fn test_fatal() {
148 fatal!("This is a fatal error");
149 }
150
151 #[test]
152 #[should_panic]
153 fn test_debug_fatal() {
154 if cfg!(debug_assertions) {
155 debug_fatal!("This is a debug fatal error");
156 } else {
157 fatal!("This is a fatal error");
159 }
160 }
161
162 #[cfg(not(debug_assertions))]
163 #[test]
164 fn test_debug_fatal_release_mode() {
165 debug_fatal!("This is a debug fatal error");
166 }
167
168 #[test]
169 fn test_assert_sometimes_side_effects() {
170 let mut x = 0;
171
172 let mut inc = || {
173 x += 1;
174 true
175 };
176
177 assert_sometimes!(inc(), "");
178 assert_eq!(x, 1);
179 }
180}