|
4 | 4 | use crate::backtrace_rs::{self, BacktraceFmt, BytesOrWideString, PrintFmt}; |
5 | 5 | use crate::borrow::Cow; |
6 | 6 | use crate::io::prelude::*; |
| 7 | +use crate::mem::{ManuallyDrop, MaybeUninit}; |
7 | 8 | use crate::path::{self, Path, PathBuf}; |
8 | 9 | use crate::sync::{Mutex, MutexGuard, PoisonError}; |
9 | 10 | use crate::{env, fmt, io}; |
@@ -81,12 +82,26 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: |
81 | 82 | let frame_ip = frame.ip(); |
82 | 83 | res = writeln!(bt_fmt.formatter(), "{idx:4}: {frame_ip:HEX_WIDTH$?}"); |
83 | 84 | } else { |
| 85 | + // `call_with_end_short_backtrace_marker` means we are done hiding symbols |
| 86 | + // for now. Print until we see `call_with_begin_short_backtrace_marker`. |
| 87 | + if print_fmt == PrintFmt::Short { |
| 88 | + let sym = frame.symbol_address(); |
| 89 | + if sym == call_with_end_short_backtrace_marker as _ { |
| 90 | + print = true; |
| 91 | + return true; |
| 92 | + } else if print && sym == call_with_begin_short_backtrace_marker as _ { |
| 93 | + print = false; |
| 94 | + return true; |
| 95 | + } |
| 96 | + } |
| 97 | + |
84 | 98 | let mut hit = false; |
85 | 99 | backtrace_rs::resolve_frame_unsynchronized(frame, |symbol| { |
86 | 100 | hit = true; |
87 | 101 |
|
88 | | - // `__rust_end_short_backtrace` means we are done hiding symbols |
89 | | - // for now. Print until we see `__rust_begin_short_backtrace`. |
| 102 | + // Hide `__rust_[begin|end]_short_backtrace` frames from short backtraces. |
| 103 | + // Unfortunately these generic functions have to be matched by name, as we do |
| 104 | + // not know their generic parameters. |
90 | 105 | if print_fmt == PrintFmt::Short { |
91 | 106 | if let Some(sym) = symbol.name().and_then(|s| s.as_str()) { |
92 | 107 | if sym.contains("__rust_end_short_backtrace") { |
@@ -155,36 +170,60 @@ unsafe fn _print_fmt(fmt: &mut fmt::Formatter<'_>, print_fmt: PrintFmt) -> fmt:: |
155 | 170 | Ok(()) |
156 | 171 | } |
157 | 172 |
|
158 | | -/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that |
159 | | -/// this is only inline(never) when backtraces in std are enabled, otherwise |
160 | | -/// it's fine to optimize away. |
161 | | -#[cfg_attr(feature = "backtrace", inline(never))] |
162 | | -pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T |
163 | | -where |
164 | | - F: FnOnce() -> T, |
165 | | -{ |
166 | | - let result = f(); |
| 173 | +macro_rules! short_backtrace_controls { |
| 174 | + ($($adapter:ident => $marker:ident($unique:literal)),* $(,)?) => {$( |
| 175 | + /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that |
| 176 | + /// this is only inline(never) when backtraces in std are enabled, otherwise |
| 177 | + /// it's fine to optimize away. |
| 178 | + /// |
| 179 | + /// It is guaranteed that `f` will be called exactly once, and `unsafe` code may |
| 180 | + /// rely on this to be the case. |
| 181 | + #[cfg_attr(feature = "backtrace", inline(never))] |
| 182 | + fn $marker(f: &mut dyn FnMut()) { |
| 183 | + f(); |
| 184 | + |
| 185 | + // (Try to) prevent both Identical Code Folding (which might merge the different |
| 186 | + // versions of this function, giving them the same address) and Tail Call Optimisation |
| 187 | + // (which could remove their frames from the call stack). |
| 188 | + crate::hint::black_box($unique); |
| 189 | + } |
167 | 190 |
|
168 | | - // prevent this frame from being tail-call optimised away |
169 | | - crate::hint::black_box(()); |
| 191 | + /// Invokes `$marker` with an adaptation of `f`, returning its result. |
| 192 | + /// This is a more ergonomic interface for placing the marker frame on the stack than |
| 193 | + /// the `$marker` function itself. It can be inlined without problem. |
| 194 | + #[doc(hidden)] |
| 195 | + #[unstable( |
| 196 | + feature = "short_backtrace_controls", |
| 197 | + reason = "to control abbreviation of backtraces", |
| 198 | + issue = "none" |
| 199 | + )] |
| 200 | + #[inline(always)] |
| 201 | + pub fn $adapter<F, T>(f: F) -> T |
| 202 | + where |
| 203 | + F: FnOnce() -> T, |
| 204 | + { |
| 205 | + let mut result = MaybeUninit::<T>::uninit(); |
| 206 | + let mut f = ManuallyDrop::new(f); |
170 | 207 |
|
171 | | - result |
172 | | -} |
| 208 | + let mut adapted = || { |
| 209 | + // SAFETY: `adapted` is called exactly once, by `$marker`; |
| 210 | + // and the `ManuallyDrop` is not otherwise used again. |
| 211 | + let f = unsafe { ManuallyDrop::take(&mut f) }; |
| 212 | + result.write(f()); |
| 213 | + }; |
173 | 214 |
|
174 | | -/// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`. Note that |
175 | | -/// this is only inline(never) when backtraces in std are enabled, otherwise |
176 | | -/// it's fine to optimize away. |
177 | | -#[cfg_attr(feature = "backtrace", inline(never))] |
178 | | -pub fn __rust_end_short_backtrace<F, T>(f: F) -> T |
179 | | -where |
180 | | - F: FnOnce() -> T, |
181 | | -{ |
182 | | - let result = f(); |
| 215 | + $marker(&mut adapted); |
183 | 216 |
|
184 | | - // prevent this frame from being tail-call optimised away |
185 | | - crate::hint::black_box(()); |
| 217 | + // SAFETY: `$marker` guaranteed that it would call `adapted`, which |
| 218 | + // initialized `result`. |
| 219 | + unsafe { result.assume_init() } |
| 220 | + } |
| 221 | + )*}; |
| 222 | +} |
186 | 223 |
|
187 | | - result |
| 224 | +short_backtrace_controls! { |
| 225 | + __rust_begin_short_backtrace => call_with_begin_short_backtrace_marker(0), |
| 226 | + __rust_end_short_backtrace => call_with_end_short_backtrace_marker(1), |
188 | 227 | } |
189 | 228 |
|
190 | 229 | /// Prints the filename of the backtrace frame. |
|
0 commit comments