solana_msg_utils/
lib.rs

1//! Provides macros used for logging messages on-chain, with
2//! the ability to provide "trace level" logging messages. 
3//! 
4//! In addition to this, the trace level logging messages utilizes
5//! stack based formatting if the message size is less than 512 bytes
6//! for maximal compute units consumption efficiency
7
8/// msg_panic! is a wrapper around the `msg!` and `panic!`
9/// macros used to log an error message, and panic in bpf environments
10/// which do not actually show a message emitted by a panic macro
11#[macro_export]
12macro_rules! msg_panic {
13    ($($args:tt)+) => {{
14        // the actual error message
15        solana_program::msg!("RUNTIME ERROR: {}", format_args!($($args)*));
16        // panic to indicate the line the error came from
17        // but panicking doesn't log anything in bpf target for solana programs
18        panic!("RUNTIME ERROR: {}", format_args!($($args)*));
19    }};
20}
21
22#[macro_export]
23macro_rules! sum {
24    // this delcares an exrpession i think :shrug:
25    // todo(): explain this more
26    ($($args:expr),*) => {{
27        let mut result = 0;
28        $(
29            // combine the size of each value
30            result = result + $args.len();
31        )*
32        // return the size of all arguments
33        result
34    }}
35}
36
37/// msg_trace! is a wrapper around the `msg!` macro, that faciliates logging trace
38/// level logs, which include the file and line number from where the message was emitted.
39///
40/// if the total msg size is less than or equal to 512 bytes, then `arrform!` is used for
41/// the optimize (heap-less) message formatting. messages larger than 512 bytes use the traditional `format!`.
42#[macro_export]
43macro_rules! msg_trace {
44    ($($args:tt)+) => {
45        // get the filename that produce the log, it's less info than the fille path
46        // but it saves pace, an when paired with the line number is more than enough debug
47        let file_name = std::path::Path::new(file!()).file_name().unwrap().to_string_lossy();
48        let input_sizes = sum!($($args)*);
49        solana_program::msg!("input sizes {}", input_sizes);
50        if input_sizes > 512 {
51            // slow path
52            solana_program::msg!("{}", format!("'{}', '{}:{}", format!($($args)*), file_name, line!()).as_str());
53        } else {
54            use tulip_arrform::{arrform, ArrForm};
55            let file_info = arrform!(256, "{}:{}", file_name, line!());
56            let msg_part = arrform!(512, $($args)*);
57            solana_program::msg!("'{}', {}", msg_part.as_str(), file_info.as_str());
58        }
59    };
60}
61
62
63#[cfg(test)]
64mod test {
65    use super::*;
66    use solana_program::msg;
67    #[test]
68    fn test_trace() {
69        {
70            msg_trace!("hello world this is {}", "very big message");
71        }
72        {
73            let mut msg_str = String::new();
74            for _ in 0..550 {
75                msg_str.push('a');
76            }
77            msg_trace!("{}", msg_str);
78        }
79    }
80    #[test]
81    #[should_panic(expected = "RUNTIME ERROR: too many keks")]
82    fn test_msg_panic() {
83        msg_panic!("too many keks");
84    }
85}