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 unsafe { std::env::set_var(name, value) };
58 }
59 }
60}
61
62#[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 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}