Closed fraillt closed 6 months ago
I think the motivation you have is good. But I feel slightly nervous about exposing &'a mut [BacktraceFrame]
and thus &'a mut BacktraceFrame
, allowing mutations that can make the backtrace inconsistent. This is partly because I've noticed the problems you observe, and I think your direction overall is a good idea: making this crate's backtrace lazier is clearly the "way to go".
But exposing the entire slice mutably, and without resolving first, is potentially problematic for later optimizations and better interfaces that might rely on internally avoiding doing work where unnecessary, but now would have to account for "you might have called frames_mut
and done something with them first". So I think addressing the needs you've cited might be better done by adding a function to Backtrace that returns an iterator that lazily resolves a BacktraceFrame on iteration.
@workingjubilee Thanks for your insights, it makes sense.
I tried to implement lazy iterator, but it's not straightforward as it requires interior mutability.
I noticed that we already have From and Into<Vec<BacktraceFrame>> for Backtrace
so I guess we can simply provide a way to resolve each frame manually.
(I updated revision with minimum changes)
Below is the size of a hello-world Rust program linked with libstd with backtrace.
Original binary size: 518984B Updated binary size: 519008B Difference: +24B (0.00%)
Summary of all changes so far:
BacktraceFrame::resolve(&mut self)
(this is the essence of the PR)impl Into<Vec<BacktraceFrame>> for Backtrace
with impl From<Backtrace> for Vec<BacktraceFrame>
One should avoid implementing Into and implement From instead. (source)
Backtrace::frames(&self)
returns same frames as in impl From<Backtrace> for Vec<BacktraceFrame>
.replaced
impl Into<Vec<BacktraceFrame>> for Backtrace
withimpl From<Backtrace> for Vec<BacktraceFrame>
I know that's the "correct" style, but we've had several breakages due to "fixing" this, because this crate gets included directly into std, and sometimes rustc gets confused as to whether impls in this crate are publicly accessible, especially when it comes to resolving type inference.
So, unfortunately, I must tell you: Do it wrong.
Thanks for fast replay! but I really thing that impl From<Backtrace> for Vec<BacktraceFrame>
is not an issue. There's my reasoning.
There's conversion only from Backtrace to BacktraceFrame:
If this is really a problem, then: a) I think the problem is elsewhere, and we should fix that problem instead :) b) we should be able to "proof" that there's a problem, revert it back and add big comment in code, explaining exactly that is the issue.
Type inference here should be pretty easy here, and there are a lot of code like this in the the wild. I really cannot see how this could interfere with other type coercions... :(
Part of why it is not disruptive "in the wild" is because there either already is two implementations to infer types from, or all the types to infer based on local implementations are local and it resolves one of those. There is an unfortunate quirk where if there is only one implementation it is always inferred by default, even though it would be ambiguous if there were two in scope. Thus adding additional directions for Vec<T>
can weaken type inference even though, by all rights, if the type inference algorithm did not have this quirk, it would not infer in the first place (and possibly instead use some other information to guide inference).
std is unique in appearing in what rounds off to every single crate.
Specifically, Vec<T>
is in scope in every single crate.
So I agree the problem is elsewhere, but I did not want to block accepting this PR on landing a new type inference algorithm in rustc, or making sure type resolution + inference never can "leak" in a more "by construction" manner than how it is currently done.
Code size changes for a hello-world Rust program linked with libstd with backtrace:
On platform windows-latest
:
Hello,
Resolving backtrace (at least for the first time) is very slow, but this is not something that can be solved (I guess). However, not all frames are equally useful, and at least in the code base that I'm working on, we simply ignore bunch of them. We have code something like this:
For us, it takes takes around ~700ms (sometimes even ~1.5s) to resolve whole backtrace.
With this PR we're able to write like this:
And it takes around 25ms to resolve the frames that we actually need.
I'm aware of low level utils
backtrace::trace
andbacktrace::resolve_frame
, but this would basically mean that I would basically need to reimplement Backtrace, including Serialize/Deserialize capability, which is too much.