[Pitch] Swift Backtracing API

I love the idea of capturing the presence of a discontinuity. That's exactly what you might want. Honestly, even without that in the API, it would still produce more useful, if slightly dishonest, backtrace. The whole repeated frame thing was garbage. But, it was easy to do, and was immensely helpful in practice.

You might also want to consider removing the limit altogether and make that an implementation detail. Clients won't really have a way of knowing what a reasonable limit is anyways - they'll likely either just leave it the default or make it unlimited. Crash reports on Darwin have a much smaller, predefined limit and that works great for basically all cases except infinite recursion.

1 Like

Yes, fundamentally the problem is that Backtrace is full of unsafe pointers to (code) objects with a lifetime we usually can't guarantee. Strictly speaking, it's not even correct to assume that the return addresses currently in the backtrace are still all mapped, or mapped to what they were at the point the function was executing. I also wonder if there are use-after-free-like issues with grabbing a backtrace to symbolicate later with DWARF, since DWARF unwinding is Turing-complete...

Backtrace is currently structured as a precursor to SymbolicatedBacktrace. IMO, we could improve the situation by having both Backtrace and SymbolicatedBacktrace have a capture method and moving images off of Backtrace. I would say that it's true regardless of the details of turning a Backtrace into a SymbolicatedBacktrace.

One way I think that we could let you turn Backtraces into SymbolicatedBacktraces fairly safely would be to have something like:

withImageSnapshot { snapshot in
    let backtrace = callDeepAndReturnTrace()
    let symbolicated = snapshot.symbolicate(backtrace)
}

where we would have to ensure that snapshot does not escape, and do nothing for frames that don't lie in images captured by the snapshot.

There's some precedent in the standard library for this sort of tuple return (where one tuple element is a flag describing the other element):

3 Likes

I don't think this is as big a problem as you might think. SymbolicatedBacktrace has to locate the image files on disk and read symbols from them there, because the debug information doesn't get mapped into address space on most systems. The only time we access information using addresses we've obtained while backtracing is during backtracing, at which point the call stack is still threaded so there's no way that things are getting unmapped.

I've also been pondering the images thing some more. For frame-pointer-based unwinding, we don't need the image list to do the unwind, but for anything fancier we do (or, at least, we need to find the images corresponding to frames in the call stack) because we need to locate the unwind information. So I think it's right that Backtrace should be able to capture images — but it should probably be optional and maybe there should be a separate function/method to capture them after the event. I think making it a lazy var might have been the wrong choice, because it hides when that happens and I think you're right that it's important that people at least know when they're doing that capture (if it's after the backtracing).

2 Likes