【问题标题】:Transform backtrace to string during catch_unwind在 catch_unwind 期间将回溯转换为字符串
【发布时间】:2021-07-28 08:20:54
【问题描述】:

我正在用 Rust 编写一个跨平台(Linux/iOS/Android)库。万一我的库出现恐慌,我希望应用程序继续正常工作而不是崩溃。为此,我使用catch_unwind;它的结果包含恐慌信息,并且没有包含有关问题根源的足够信息,这是一段解释我在做什么的代码:

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"));
}

Playground

万一发生错误,返回的消息是不够的,有时只包含消息

未知的错误来源

我想将回溯发送到调用源,以便它可以记录它,然后我们可以在之后调试 Rust 库。我该怎么做?

【问题讨论】:

    标签: rust error-handling backtrace


    【解决方案1】:

    有时它会忽略消息:

    未知的错误来源

    这可能是因为e可能不仅是String,还可能是&amp;str(嗯,基本上可以是任何类型,但是这两个是最常见的,你只能得到其他类型时使用panic_any())。当在catch_unwind() 中调用panic!() 时,如果你是带有参数的panic!,你只会得到String,否则,恐慌消息是&amp;str(因为不需要分配String )。

    我想将回溯发送给调用源,以便他可以记录它,然后我们可以调试 rust 库,我该怎么做?

    夜间 Rust 的解决方案

    在夜间 Rust 中,您可以通过 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(""));
    }
    

    Playground link

    输出:

    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
    

    请注意,在调用 panic! 之后,此回溯包含一些额外的帧,这是由于 Rust 的内部恐慌处理代码。

    有依赖的解决方案

    如果你不是 nightly Rust,但愿意使用额外的依赖项,你可以对 backtrace crate 做同样的事情(感谢 @BashirAbdelwahed 引起我的注意):

    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(""));
    }
    

    Playground link

    输出:

    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
    

    【讨论】:

    • 但是包含在生产二进制文件中是个好主意吗?发布模式怎么样?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-08-31
    相关资源
    最近更新 更多