rust-lang / project-error-handling

Error handling project group
Apache License 2.0
268 stars 18 forks source link

Non allocating / low memory usage backtrace printing interface #35

Open yaahc opened 3 years ago

yaahc commented 3 years ago

Right now std has proposed stabilizing a very minimal interface for capturing and printing Backtraces. However, internally it does not use this same interface in it's own panic hook. Internally, instead of capturing a Backtrace object and then printing it std directly invokes a function that prints the backtrace without capturing or resolving frames in advance (source). This interface apparently uses much less memory (does it allocate heap memory at all?).

Other contributors to the project have expressed a desire for a lightweight backtrace printing interface they can use from signal handlers and the like (source). The above interface doesn't seem to work in this specific scenario, but we may want to look into what would be needed to print a backtrace from a signal handler, and in the future possibly expose alternate interfaces to printing backtraces that could work in these constrained environments.

burdges commented 3 years ago

Can error types be !Send and/or !Sync?

yaahc commented 3 years ago

Can error types be !Send and/or !Sync?

yes, how does that apply here?

edit: actually, yes depending on what you mean by "be !Send". If you're referring to negative trait bounds then no, but if you mean can errors not implement Send or Sync and still implement the Error trait then absolutely, though many parts of the ecosystem assume that all error types are Send + Sync + 'static, so we advise against making errors that aren't Send/Sync/'static, or at least suggest that you have a conversion method for getting an owned + Send + Sync version of those errors.

Amanieu commented 3 years ago

I just published mini-backtrace which allows capturing backtraces on no_std/embedded targets. It does not use any dynamic memory allocation, and resolving symbol names & line numbers is done offline using addr2line.

Internally it uses LLVM's libunwind built with special flags and stripped of all OS/libc dependencies.

yaahc commented 3 years ago

That's amazing @Amanieu, do you think the approach used in mini-backtrace could eventually be applied upstream to std::backtrace::Backtrace?

Amanieu commented 3 years ago

Not really, unwinding/backtraces fundamentally relies on some system-level support to locate unwinding information for the current binary. mini-backtrace compiles its own libunwind with -D_LIBUNWIND_IS_BAREMETAL which causes it to use the special symbols __exidx_* to locate unwinding metadata in memory. This requires using special linker scripts to set up and only works for the main executable, not any shared libraries.

Resolving symbols and line numbers requires reading debuginfo which is not loaded to memory, which is not possible in no_std. mini-backtrace just punts symbol resolution to an offline step.

thomcc commented 2 years ago

It would also be desirable to use something like this in signal handlers. This is sometimes[^1] supported by C/C++ backtrace libs (a concrete example is that abseil's has it) since otherwise it's dubious (very unsafe) to use in cases like a segfault handler that tells you where the bad pointer use happened.

It may be more trouble than it's worth though... It is certainly less critical for Rust, where bad pointer dereferences are not nearly as common.

[^1]: I almost said commonly here, but it's not clear to me if its actually commonly supported, or if people just commonly do it anyway. (Realistically, you can get away with doing a lot of signal-unsafe things in signal handlers...)

sfackler commented 2 years ago

I'm currently using the libunwind project to backtrace in a signal handler since it's the only standalone one I'm aware of that claims to be async-signal safe. It would be amazing if the standard version offered that capability.

Amanieu commented 2 years ago

I'm currently using the libunwind project to backtrace in a signal handler since it's the only standalone one I'm aware of that claims to be async-signal safe.

It's not because it uses dl_iterate_phdr from the dynamic linker to locate unwinding information. This function takes locks and allocates memory.

sfackler commented 2 years ago

Yeah their documentation seems overly optimistic. Abseil's may be more accurate, but it appears to rely on frame pointers which is not really a realistic option unless you're building your entire userspace...