mysten_common/
assert_reachable.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use once_cell::sync::Lazy;
5use std::io::Write;
6use std::path::PathBuf;
7use std::sync::Mutex;
8
9pub struct ReachableAssertion {
10    pub assertion_type: &'static str,
11    pub loc: &'static str,
12    pub msg: &'static str,
13}
14
15static REACHABLE_LOG_DIR: Lazy<Option<PathBuf>> = Lazy::new(|| {
16    std::env::var("MSIM_LOG_REACHABLE_ASSERTIONS")
17        .ok()
18        .map(PathBuf::from)
19        .filter(|p| p.is_dir())
20});
21
22static REACHABLE_LOG_FILE: Lazy<Option<Mutex<std::fs::File>>> = Lazy::new(|| {
23    let dir = REACHABLE_LOG_DIR.as_ref()?;
24    let seed = std::env::var("MSIM_TEST_SEED").unwrap_or_else(|_| "unknown".to_string());
25    let filename = format!("{}.reached", seed);
26    let path = dir.join(filename);
27    std::fs::OpenOptions::new()
28        .create(true)
29        .append(true)
30        .open(path)
31        .ok()
32        .map(Mutex::new)
33});
34
35static SOMETIMES_LOG_FILE: Lazy<Option<Mutex<std::fs::File>>> = Lazy::new(|| {
36    let dir = REACHABLE_LOG_DIR.as_ref()?;
37    let seed = std::env::var("MSIM_TEST_SEED").unwrap_or_else(|_| "unknown".to_string());
38    let filename = format!("{}.sometimes", seed);
39    let path = dir.join(filename);
40    std::fs::OpenOptions::new()
41        .create(true)
42        .append(true)
43        .open(path)
44        .ok()
45        .map(Mutex::new)
46});
47
48pub fn log_reached_assertion(loc: &'static str) {
49    if let Some(file) = REACHABLE_LOG_FILE.as_ref()
50        && let Ok(mut f) = file.lock()
51    {
52        let _ = writeln!(f, "{}", loc);
53    }
54}
55
56pub fn log_sometimes_assertion(loc: &'static str) {
57    if let Some(file) = SOMETIMES_LOG_FILE.as_ref()
58        && let Ok(mut f) = file.lock()
59    {
60        let _ = writeln!(f, "{}", loc);
61    }
62}
63
64#[macro_export]
65macro_rules! assert_reachable_simtest_impl {
66    ($assertion_type:literal, $condition:expr, $message:literal, $log_fn:path) => {{
67        use std::sync::atomic::{AtomicBool, Ordering};
68        use $crate::assert_reachable::ReachableAssertion;
69
70        const LOC: &str = concat!(file!(), ":", line!(), ":", column!());
71
72        #[used]
73        #[cfg_attr(
74            any(target_os = "linux", target_os = "android"),
75            unsafe(link_section = ".reach_points")
76        )]
77        #[cfg_attr(target_os = "macos", unsafe(link_section = "__DATA,__reach_points"))]
78        #[cfg_attr(target_os = "windows", unsafe(link_section = ".reach_points"))]
79        static RP: ReachableAssertion = ReachableAssertion {
80            assertion_type: $assertion_type,
81            loc: LOC,
82            msg: $message,
83        };
84
85        if $condition {
86            static LOGGED: AtomicBool = AtomicBool::new(false);
87            if !LOGGED.swap(true, Ordering::Relaxed) {
88                $log_fn(LOC);
89            }
90        }
91    }};
92}
93
94#[macro_export]
95macro_rules! assert_reachable_simtest {
96    ($message:literal) => {
97        $crate::assert_reachable_simtest_impl!(
98            "reachable",
99            true,
100            $message,
101            $crate::assert_reachable::log_reached_assertion
102        )
103    };
104}
105
106#[macro_export]
107macro_rules! assert_sometimes_simtest {
108    ($expr:expr, $message:literal) => {
109        $crate::assert_reachable_simtest_impl!(
110            "sometimes",
111            $expr,
112            $message,
113            $crate::assert_reachable::log_sometimes_assertion
114        )
115    };
116}