openmina/
main.rs

1use backtrace::Backtrace;
2use std::panic::PanicHookInfo;
3
4#[cfg(not(target_arch = "wasm32"))]
5use tikv_jemallocator::Jemalloc;
6
7#[cfg(not(target_arch = "wasm32"))]
8#[global_allocator]
9static GLOBAL: Jemalloc = Jemalloc;
10
11pub mod commands;
12use clap::Parser;
13
14mod exit_with_error;
15pub use exit_with_error::exit_with_error;
16
17#[cfg(feature = "unsafe-signal-handlers")]
18mod unsafe_signal_handlers {
19    use nix::libc;
20
21    extern "C" fn handle_sigsegv(_signal: libc::c_int) {
22        eprintln!("########### SIGSEGV #############");
23        node::recorder::Recorder::graceful_shutdown();
24        std::process::exit(1);
25    }
26
27    pub fn setup() {
28        let stack_t = libc::stack_t {
29            ss_sp: {
30                let stack = Box::<[u8; libc::SIGSTKSZ]>::new([0; libc::SIGSTKSZ]);
31                Box::into_raw(stack) as *mut _
32            },
33            ss_flags: 0,
34            ss_size: libc::SIGSTKSZ,
35        };
36
37        let res = unsafe { libc::sigaltstack(&stack_t as *const _, std::ptr::null_mut()) };
38        assert_eq!(res, 0);
39
40        let action = libc::sigaction {
41            sa_sigaction: handle_sigsegv as _,
42            sa_mask: unsafe { std::mem::zeroed() },
43            sa_flags: libc::SA_ONSTACK,
44            sa_restorer: None,
45        };
46        let res = unsafe { libc::sigaction(libc::SIGSEGV, &action as _, std::ptr::null_mut()) };
47        assert_eq!(res, 0);
48    }
49}
50
51fn setup_var_from_single_and_only_thread() {
52    const VARS: &[(&str, &str)] = &[("RUST_BACKTRACE", "full")];
53
54    for (name, value) in VARS {
55        if std::env::var(name).is_err() {
56            // Safe to call, we didn't launch any threads yet
57            unsafe { std::env::set_var(name, value) };
58        }
59    }
60}
61
62/// Mimic default hook:
63/// <https://github.com/rust-lang/rust/blob/5986ff05d8480da038dd161b3a6aa79ff364a851/library/std/src/panicking.rs#L246>
64///
65/// Unlike the default hook, this one allocates.
66/// We store (+ display) panics in non-main threads, and display them all when the main thread panics.
67#[cfg(not(target_family = "wasm"))]
68fn new_hook(info: &PanicHookInfo<'_>) {
69    use std::{any::Any, io::Write};
70
71    fn payload_as_str(payload: &dyn Any) -> &str {
72        if let Some(&s) = payload.downcast_ref::<&'static str>() {
73            s
74        } else if let Some(s) = payload.downcast_ref::<String>() {
75            s.as_str()
76        } else {
77            "Box<dyn Any>"
78        }
79    }
80
81    static PREVIOUS_PANICS: std::sync::Mutex<Vec<Vec<u8>>> =
82        const { std::sync::Mutex::new(Vec::new()) };
83
84    let mut s: Vec<u8> = Vec::with_capacity(64 * 1024);
85    let backtrace = Backtrace::new();
86
87    let current = std::thread::current();
88    let name = current.name().unwrap_or("<unnamed>");
89    let location = info.location().unwrap();
90    let msg = payload_as_str(info.payload());
91
92    let _ = writeln!(&mut s, "\nthread '{name}' panicked at {location}:\n{msg}");
93    let _ = writeln!(&mut s, "{:#?}", &backtrace);
94
95    eprintln!("{}", String::from_utf8_lossy(&s));
96
97    if name != "main" {
98        let Ok(mut previous) = PREVIOUS_PANICS.lock() else {
99            return;
100        };
101        // Make sure we don't store too many panics
102        if previous.len() < 256 {
103            previous.push(s);
104            eprintln!("Saved panic from thread '{name}'");
105        } else {
106            eprintln!("Panic from thread '{name}' not saved !");
107        }
108    } else {
109        let Ok(previous) = PREVIOUS_PANICS.lock() else {
110            return;
111        };
112        eprintln!("\nNumber of panics from others threads: {}", previous.len());
113        for panic in previous.iter() {
114            eprintln!("{}", String::from_utf8_lossy(panic));
115        }
116    }
117}
118
119fn early_setup() {
120    setup_var_from_single_and_only_thread();
121    #[cfg(not(target_family = "wasm"))]
122    std::panic::set_hook(Box::new(new_hook));
123}
124
125fn main() -> anyhow::Result<()> {
126    early_setup();
127
128    #[cfg(feature = "unsafe-signal-handlers")]
129    unsafe_signal_handlers::setup();
130    let app = commands::OpenminaCli::parse();
131
132    let network_init_result = match app.network {
133        commands::Network::Devnet => openmina_core::NetworkConfig::init("devnet"),
134        commands::Network::Mainnet => openmina_core::NetworkConfig::init("mainnet"),
135    };
136
137    network_init_result.expect("Failed to initialize network configuration");
138
139    app.command.run()
140}