1 /// Common code for printing the backtrace in the same way across the different
2 /// supported platforms.
7 use path
::{self, Path}
;
9 use rustc_demangle
::demangle
;
11 use sync
::atomic
::{self, Ordering}
;
12 use sys
::mutex
::Mutex
;
14 pub use sys
::backtrace
::{
17 foreach_symbol_fileline
,
21 #[cfg(target_pointer_width = "64")]
22 pub const HEX_WIDTH
: usize = 18;
24 #[cfg(target_pointer_width = "32")]
25 pub const HEX_WIDTH
: usize = 10;
27 /// Represents an item in the backtrace list. See `unwind_backtrace` for how
29 #[derive(Debug, Copy, Clone)]
31 /// Exact address of the call that failed.
32 pub exact_position
: *const u8,
33 /// Address of the enclosing function.
34 pub symbol_addr
: *const u8,
35 /// Which inlined function is this frame referring to
36 pub inline_context
: u32,
39 /// Max number of frames to print.
40 const MAX_NB_FRAMES
: usize = 100;
42 /// Prints the current backtrace.
43 pub fn print(w
: &mut dyn Write
, format
: PrintFormat
) -> io
::Result
<()> {
44 static LOCK
: Mutex
= Mutex
::new();
46 // There are issues currently linking libbacktrace into tests, and in
47 // general during libstd's own unit tests we're not testing this path. In
48 // test mode immediately return here to optimize away any references to the
49 // libbacktrace symbols
54 // Use a lock to prevent mixed output in multithreading context.
55 // Some platforms also requires it, like `SymFromAddr` on Windows.
58 let res
= _print(w
, format
);
64 fn _print(w
: &mut dyn Write
, format
: PrintFormat
) -> io
::Result
<()> {
65 let mut frames
= [Frame
{
66 exact_position
: ptr
::null(),
67 symbol_addr
: ptr
::null(),
70 let (nb_frames
, context
) = unwind_backtrace(&mut frames
)?
;
71 let (skipped_before
, skipped_after
) =
72 filter_frames(&frames
[..nb_frames
], format
, &context
);
73 if skipped_before
+ skipped_after
> 0 {
74 writeln
!(w
, "note: Some details are omitted, \
75 run with `RUST_BACKTRACE=full` for a verbose backtrace.")?
;
77 writeln
!(w
, "stack backtrace:")?
;
79 let filtered_frames
= &frames
[..nb_frames
- skipped_after
];
80 for (index
, frame
) in filtered_frames
.iter().skip(skipped_before
).enumerate() {
81 resolve_symname(*frame
, |symname
| {
82 output(w
, index
, *frame
, symname
, format
)
84 let has_more_filenames
= foreach_symbol_fileline(*frame
, |file
, line
| {
85 output_fileline(w
, file
, line
, format
)
87 if has_more_filenames
{
88 w
.write_all(b
" <... and possibly more>")?
;
95 /// Returns a number of frames to remove at the beginning and at the end of the
96 /// backtrace, according to the backtrace format.
97 fn filter_frames(frames
: &[Frame
],
99 context
: &BacktraceContext
) -> (usize, usize)
101 if format
== PrintFormat
::Full
{
105 let skipped_before
= 0;
107 let skipped_after
= frames
.len() - frames
.iter().position(|frame
| {
108 let mut is_marker
= false;
109 let _
= resolve_symname(*frame
, |symname
| {
110 if let Some(mangled_symbol_name
) = symname
{
111 // Use grep to find the concerned functions
112 if mangled_symbol_name
.contains("__rust_begin_short_backtrace") {
119 }).unwrap_or(frames
.len());
121 if skipped_before
+ skipped_after
>= frames
.len() {
122 // Avoid showing completely empty backtraces
126 (skipped_before
, skipped_after
)
130 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
132 pub fn __rust_begin_short_backtrace
<F
, T
>(f
: F
) -> T
133 where F
: FnOnce() -> T
, F
: Send
, T
: Send
138 /// Controls how the backtrace should be formatted.
139 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
140 pub enum PrintFormat
{
141 /// Show only relevant data from the backtrace.
143 /// Show all the frames with absolute path for files.
147 // For now logging is turned off by default, and this function checks to see
148 // whether the magical environment variable is present to see if it's turned on.
149 pub fn log_enabled() -> Option
<PrintFormat
> {
150 static ENABLED
: atomic
::AtomicIsize
= atomic
::AtomicIsize
::new(0);
151 match ENABLED
.load(Ordering
::SeqCst
) {
154 2 => return Some(PrintFormat
::Short
),
155 _
=> return Some(PrintFormat
::Full
),
158 let val
= env
::var_os("RUST_BACKTRACE").and_then(|x
|
161 } else if &x
== "full" {
162 Some(PrintFormat
::Full
)
164 Some(PrintFormat
::Short
)
167 ENABLED
.store(match val
{
168 Some(v
) => v
as isize,
170 }, Ordering
::SeqCst
);
174 /// Prints the symbol of the backtrace frame.
176 /// These output functions should now be used everywhere to ensure consistency.
177 /// You may want to also use `output_fileline`.
178 fn output(w
: &mut dyn Write
, idx
: usize, frame
: Frame
,
179 s
: Option
<&str>, format
: PrintFormat
) -> io
::Result
<()> {
180 // Remove the `17: 0x0 - <unknown>` line.
181 if format
== PrintFormat
::Short
&& frame
.exact_position
== ptr
::null() {
185 PrintFormat
::Full
=> write
!(w
,
188 frame
.exact_position
,
190 PrintFormat
::Short
=> write
!(w
, " {:2}: ", idx
)?
,
194 let symbol
= demangle(string
);
196 PrintFormat
::Full
=> write
!(w
, "{}", symbol
)?
,
197 // strip the trailing hash if short mode
198 PrintFormat
::Short
=> write
!(w
, "{:#}", symbol
)?
,
201 None
=> w
.write_all(b
"<unknown>")?
,
206 /// Prints the filename and line number of the backtrace frame.
208 /// See also `output`.
210 fn output_fileline(w
: &mut dyn Write
,
213 format
: PrintFormat
) -> io
::Result
<()> {
214 // prior line: " ##: {:2$} - func"
217 PrintFormat
::Full
=> write
!(w
,
221 PrintFormat
::Short
=> write
!(w
, " ")?
,
224 let file
= str::from_utf8(file
).unwrap_or("<unknown>");
225 let file_path
= Path
::new(file
);
226 let mut already_printed
= false;
227 if format
== PrintFormat
::Short
&& file_path
.is_absolute() {
228 if let Ok(cwd
) = env
::current_dir() {
229 if let Ok(stripped
) = file_path
.strip_prefix(&cwd
) {
230 if let Some(s
) = stripped
.to_str() {
231 write
!(w
, " at .{}{}:{}", path
::MAIN_SEPARATOR
, s
, line
)?
;
232 already_printed
= true;
237 if !already_printed
{
238 write
!(w
, " at {}:{}", file
, line
)?
;