I am writing a cross-platform (Linux/iOS/Android) library in Rust. In case my library panics I want the application to continue working as normal and not crash. In order to do that, I am using catch_unwind
; its result contains panic information and doesn't contain enough information about the source of the problem, here is a piece of code explaining what I am doing:
fn calculate(json: &str) -> String {
unimplemented!()
}
#[no_mangle]
pub fn rust_calculation(json: &str) -> String {
let r = std::panic::catch_unwind(||{
// rust calculation
let calc: String = calculate(json).into();
calc
});
let r_str = match r {
Ok(v) => v,
Err(e) => {
let panic_information = match e.downcast::<String>() {
Ok(v) => *v,
_ => "Unknown Source of Error".to_owned()
};
panic_information
}
};
return r_str;
}
fn main() {
println!("{}", rust_calculation("test"));
}
In case an error occurs, the message returned is not sufficient, and sometimes, it only contains the message
Unknown Source of Error
I want to send the backtrace to the calling source so that it can log it and then we can debug the Rust library afterwards. How can I do that?
some times it countains the message:
Unknown Source of Error
This is probably because e
may not only be a String
, but also a &str
(well, it can basically be any type, but those two are the most common, you can only get other types when using panic_any()
). When calling panic!()
inside catch_unwind()
, you only get a String
if you are panic!
'ing with arguments, otherwise, the panic message is a &str
(as there is no need for allocating a String
).
I want to send the backtrace to the calling source so that he can log it and then we can debug the rust library afterwards, how can I do that ?
On nightly Rust, you can generate a backtrace inside a panic handler via std::backtrace::Backtrace
:
#![feature(backtrace)]
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
// on panic, save the backtrace here (as string)
lazy_static! {
static ref BACKTRACE: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
}
#[no_mangle]
pub fn rust_calculation(json: &str) -> String {
let r = std::panic::catch_unwind(||{
panic!("Panic");
});
let r_str = match r {
Ok(v) => v,
Err(e) => {
let panic_information = match e.downcast::<String>() {
Ok(v) => *v,
Err(e) => match e.downcast::<&str>() {
Ok(v) => v.to_string(),
_ => "Unknown Source of Error".to_owned()
}
};
format!("{}\nBacktrace:\n{}", panic_information, BACKTRACE.lock().unwrap().take().unwrap_or("<Backtrace not found>".to_string()))
}
};
return r_str;
}
fn panic_hook(_: &std::panic::PanicInfo) {
*BACKTRACE.lock().unwrap() = Some(std::backtrace::Backtrace::force_capture().to_string());
}
fn main() {
std::panic::set_hook(Box::new(panic_hook));
println!("{}", rust_calculation(""));
}
Output:
Panic
Backtrace:
0: playground::panic_hook
at ./src/main.rs:32:39
1: core::ops::function::Fn::call
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/core/src/ops/function.rs:70:5
2: std::panicking::rust_panic_with_hook
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:626:17
3: std::panicking::begin_panic::{{closure}}
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:542:9
4: std::sys_common::backtrace::__rust_end_short_backtrace
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/sys_common/backtrace.rs:141:18
5: std::panicking::begin_panic
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:541:12
6: playground::rust_calculation::{{closure}}
at ./src/main.rs:13:9
7: std::panicking::try::do_call
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:401:40
8: __rust_try
9: std::panicking::try
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:365:19
10: std::panic::catch_unwind
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panic.rs:434:14
11: rust_calculation
at ./src/main.rs:12:13
12: playground::main
at ./src/main.rs:37:20
13: core::ops::function::FnOnce::call_once
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/core/src/ops/function.rs:227:5
14: std::sys_common::backtrace::__rust_begin_short_backtrace
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/sys_common/backtrace.rs:125:18
15: std::rt::lang_start::{{closure}}
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/rt.rs:63:18
16: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/core/src/ops/function.rs:259:13
17: std::panicking::try::do_call
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:401:40
18: std::panicking::try
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:365:19
19: std::panic::catch_unwind
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panic.rs:434:14
20: std::rt::lang_start_internal::{{closure}}
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/rt.rs:45:48
21: std::panicking::try::do_call
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:401:40
22: std::panicking::try
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panicking.rs:365:19
23: std::panic::catch_unwind
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/panic.rs:434:14
24: std::rt::lang_start_internal
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/rt.rs:45:20
25: std::rt::lang_start
at /rustc/2faabf579323f5252329264cc53ba9ff803429a3/library/std/src/rt.rs:62:5
26: main
27: __libc_start_main
28: _start
Please note that this backtrace contains a few additional frames after the call to panic!
that are due to Rust's internal panic handling code.
If you are not on nightly Rust, but are willing to use additional dependencies, you can do the same with the backtrace
crate (thanks to @BashirAbdelwahed for calling my attention to this):
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex};
// on panic, save the backtrace here (as string)
lazy_static! {
static ref BACKTRACE: Arc<Mutex<Option<String>>> = Arc::new(Mutex::new(None));
}
#[no_mangle]
pub fn rust_calculation(json: &str) -> String {
let r = std::panic::catch_unwind(||{
panic!("Panic");
});
let r_str = match r {
Ok(v) => v,
Err(e) => {
let panic_information = match e.downcast::<String>() {
Ok(v) => *v,
Err(e) => match e.downcast::<&str>() {
Ok(v) => v.to_string(),
_ => "Unknown Source of Error".to_owned()
}
};
format!("{}\nBacktrace:\n{}", panic_information, BACKTRACE.lock().unwrap().take().unwrap_or("<Backtrace not found>".to_string()))
}
};
return r_str;
}
fn panic_hook(_: &std::panic::PanicInfo) {
*BACKTRACE.lock().unwrap() = Some(format!("{:?}", backtrace::Backtrace::new()));
}
fn main() {
std::panic::set_hook(Box::new(panic_hook));
println!("{}", rust_calculation(""));
}
Output:
Panic
Backtrace:
0: playground::panic_hook
at src/main.rs:31:55
1: core::ops::function::Fn::call
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/core/src/ops/function.rs:70:5
2: std::panicking::rust_panic_with_hook
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panicking.rs:595:17
3: std::panicking::begin_panic::{{closure}}
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panicking.rs:520:9
4: std::sys_common::backtrace::__rust_end_short_backtrace
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/sys_common/backtrace.rs:141:18
5: std::panicking::begin_panic
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panicking.rs:519:12
6: playground::rust_calculation::{{closure}}
at src/main.rs:12:9
7: std::panicking::try::do_call
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panicking.rs:379:40
8: __rust_try
9: std::panicking::try
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panicking.rs:343:19
10: std::panic::catch_unwind
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panic.rs:431:14
11: rust_calculation
at src/main.rs:11:13
12: playground::main
at src/main.rs:36:20
13: core::ops::function::FnOnce::call_once
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/core/src/ops/function.rs:227:5
14: std::sys_common::backtrace::__rust_begin_short_backtrace
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/sys_common/backtrace.rs:125:18
15: std::rt::lang_start::{{closure}}
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/rt.rs:49:18
16: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/core/src/ops/function.rs:259:13
std::panicking::try::do_call
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panicking.rs:379:40
std::panicking::try
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panicking.rs:343:19
std::panic::catch_unwind
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/panic.rs:431:14
std::rt::lang_start_internal
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/rt.rs:34:21
17: std::rt::lang_start
at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b/library/std/src/rt.rs:48:5
18: main
19: __libc_start_main
20: _start
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.