如何使用`catch_unwind`获取恐慌信息(即堆栈跟踪)?

4
如果使用set_hook,我们可以获得大量信息,尤其是堆栈跟踪 - 这非常有帮助。但是,使用catch_unwind,我只得到一个Result,其中几乎没有任何有用的信息。因此,我想知道如何使用Rust的catch_unwind获取 panic 信息(特别是堆栈跟踪)?
我处于一个多线程环境中,在该环境中有许多线程同时运行,并且任何一个线程都可能发生 panic。我想应该将set_hookcatch_unwind一起使用,并且还要使用一些线程本地变量,但我不确定是否可行以及具体细节。

这个回答解决了你的问题吗?从Rust钩子中的崩溃检索回溯? - Kitsu
实际上,钩子中的回溯包含它作为子回溯: 11: playground::g at src/main.rs:8:13 12: playground::main at src/main.rs:16:5对于发布模式,它是内联的,因此您可以添加#[inline(never)]以获得类似的结果。 - Kitsu
@Kitsu 我知道。问题是:我们在set_hook的回调函数中有backtrace,但我该如何将该值传输到GET_BACKTRACE_HERE位置?我可以使用ThreadLocal,但这样安全吗?(例如,在多线程或多恐慌发生时?我不熟悉恐慌) - ch271828n
1
哦,现在我明白了。这是一个有趣的问题,我认为线程本地应该可以解决:链接 - Kitsu
谢谢!您可以将其作为答案,我会接受它。 - ch271828n
显示剩余3条评论
1个回答

5
您可以使用backtrace crate在panic hook中获得回溯,但是在catch_unwind中并没有帮助,因为堆栈已经从发生panic的位置展开。
您可以通过将其存储在thread local变量中来将panic hook中的回溯“走私”到catcher中。这应该是可靠的,因为在panicking时的panic是自动进程中止(因此您无法覆盖尚未捕获的进程),并且panic不会跨线程传播(加入panicked线程会返回一个Result<_, Box<dyn Any>>,其中包含了panic作为错误)。
以下是一个完整的示例:
use std::cell::RefCell;
use backtrace::Backtrace;

thread_local! {
    static Backtrace: RefCell<Option<Backtrace>> = RefCell::new(None);
}

fn f() {
    panic!("xyz");
}

fn g() {
    if let Err(err) = std::panic::catch_unwind(f) {
        let b = Backtrace.with(|b| b.borrow_mut().take()).unwrap();
        println!("at panic:\n{:?}", b);
    }
}

fn main() {
    std::panic::set_hook(Box::new(|_| {
        let trace = Backtrace::new();
        Backtrace.with(move |b| b.borrow_mut().replace(trace));
    }));
    
    g();
}

这个能和异步一起工作吗? - Brandon Ros
@BrandonRos 是的。唯一的变化是使用futures包中的.catch_unwind(),而不是std::panic::catch_unwind - kmdreko
我已经掌握了这部分内容,但当结果是一个 Err 时,我很难从 Box<dyn Any + Send> 中获取似乎是 std::panic::PanicInfo 的信息。我正在尝试使用 catch_unwind 来获取出错的错误消息/堆栈跟踪。有什么建议吗? - Brandon Ros
1
@BrandonRos 最好通过将 Box<dyn Any> 向下转换为 &strString 来检索错误消息,尽管如果由 panic_any() 触发,则仍可能是其他内容(文档)。作为这个问题和答案的重点,无法从结果中检索回溯,但必须从 Backtrace 线程本地变量中进行访问。 - kmdreko
我希望整个PanicInfo都能够被降级,而不仅仅是其中的“payload”字段。 - Brandon Ros

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接