]> git.proxmox.com Git - rustc.git/blob - src/libstd/sys_common/backtrace.rs
New upstream version 1.27.1+dfsg1
[rustc.git] / src / libstd / sys_common / backtrace.rs
1 // Copyright 2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 /// Common code for printing the backtrace in the same way across the different
12 /// supported platforms.
13
14 use env;
15 use io::prelude::*;
16 use io;
17 use str;
18 use sync::atomic::{self, Ordering};
19 use path::{self, Path};
20 use sys::mutex::Mutex;
21 use ptr;
22
23 pub use sys::backtrace::{
24 unwind_backtrace,
25 resolve_symname,
26 foreach_symbol_fileline,
27 BacktraceContext
28 };
29
30 #[cfg(target_pointer_width = "64")]
31 pub const HEX_WIDTH: usize = 18;
32
33 #[cfg(target_pointer_width = "32")]
34 pub const HEX_WIDTH: usize = 10;
35
36 /// Represents an item in the backtrace list. See `unwind_backtrace` for how
37 /// it is created.
38 #[derive(Debug, Copy, Clone)]
39 pub struct Frame {
40 /// Exact address of the call that failed.
41 pub exact_position: *const u8,
42 /// Address of the enclosing function.
43 pub symbol_addr: *const u8,
44 /// Which inlined function is this frame referring to
45 pub inline_context: u32,
46 }
47
48 /// Max number of frames to print.
49 const MAX_NB_FRAMES: usize = 100;
50
51 /// Prints the current backtrace.
52 pub fn print(w: &mut Write, format: PrintFormat) -> io::Result<()> {
53 static LOCK: Mutex = Mutex::new();
54
55 // Use a lock to prevent mixed output in multithreading context.
56 // Some platforms also requires it, like `SymFromAddr` on Windows.
57 unsafe {
58 LOCK.lock();
59 let res = _print(w, format);
60 LOCK.unlock();
61 res
62 }
63 }
64
65 fn _print(w: &mut Write, format: PrintFormat) -> io::Result<()> {
66 let mut frames = [Frame {
67 exact_position: ptr::null(),
68 symbol_addr: ptr::null(),
69 inline_context: 0,
70 }; MAX_NB_FRAMES];
71 let (nb_frames, context) = unwind_backtrace(&mut frames)?;
72 let (skipped_before, skipped_after) =
73 filter_frames(&frames[..nb_frames], format, &context);
74 if skipped_before + skipped_after > 0 {
75 writeln!(w, "note: Some details are omitted, \
76 run with `RUST_BACKTRACE=full` for a verbose backtrace.")?;
77 }
78 writeln!(w, "stack backtrace:")?;
79
80 let filtered_frames = &frames[..nb_frames - skipped_after];
81 for (index, frame) in filtered_frames.iter().skip(skipped_before).enumerate() {
82 resolve_symname(*frame, |symname| {
83 output(w, index, *frame, symname, format)
84 }, &context)?;
85 let has_more_filenames = foreach_symbol_fileline(*frame, |file, line| {
86 output_fileline(w, file, line, format)
87 }, &context)?;
88 if has_more_filenames {
89 w.write_all(b" <... and possibly more>")?;
90 }
91 }
92
93 Ok(())
94 }
95
96 /// Returns a number of frames to remove at the beginning and at the end of the
97 /// backtrace, according to the backtrace format.
98 fn filter_frames(frames: &[Frame],
99 format: PrintFormat,
100 context: &BacktraceContext) -> (usize, usize)
101 {
102 if format == PrintFormat::Full {
103 return (0, 0);
104 }
105
106 let skipped_before = 0;
107
108 let skipped_after = frames.len() - frames.iter().position(|frame| {
109 let mut is_marker = false;
110 let _ = resolve_symname(*frame, |symname| {
111 if let Some(mangled_symbol_name) = symname {
112 // Use grep to find the concerned functions
113 if mangled_symbol_name.contains("__rust_begin_short_backtrace") {
114 is_marker = true;
115 }
116 }
117 Ok(())
118 }, context);
119 is_marker
120 }).unwrap_or(frames.len());
121
122 if skipped_before + skipped_after >= frames.len() {
123 // Avoid showing completely empty backtraces
124 return (0, 0);
125 }
126
127 (skipped_before, skipped_after)
128 }
129
130
131 /// Fixed frame used to clean the backtrace with `RUST_BACKTRACE=1`.
132 #[inline(never)]
133 pub fn __rust_begin_short_backtrace<F, T>(f: F) -> T
134 where F: FnOnce() -> T, F: Send, T: Send
135 {
136 f()
137 }
138
139 /// Controls how the backtrace should be formatted.
140 #[derive(Debug, Copy, Clone, Eq, PartialEq)]
141 pub enum PrintFormat {
142 /// Show only relevant data from the backtrace.
143 Short = 2,
144 /// Show all the frames with absolute path for files.
145 Full = 3,
146 }
147
148 // For now logging is turned off by default, and this function checks to see
149 // whether the magical environment variable is present to see if it's turned on.
150 pub fn log_enabled() -> Option<PrintFormat> {
151 static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
152 match ENABLED.load(Ordering::SeqCst) {
153 0 => {}
154 1 => return None,
155 2 => return Some(PrintFormat::Short),
156 _ => return Some(PrintFormat::Full),
157 }
158
159 let val = match env::var_os("RUST_BACKTRACE") {
160 Some(x) => if &x == "0" {
161 None
162 } else if &x == "full" {
163 Some(PrintFormat::Full)
164 } else {
165 Some(PrintFormat::Short)
166 },
167 None => None,
168 };
169 ENABLED.store(match val {
170 Some(v) => v as isize,
171 None => 1,
172 }, Ordering::SeqCst);
173 val
174 }
175
176 /// Print the symbol of the backtrace frame.
177 ///
178 /// These output functions should now be used everywhere to ensure consistency.
179 /// You may want to also use `output_fileline`.
180 fn output(w: &mut Write, idx: usize, frame: Frame,
181 s: Option<&str>, format: PrintFormat) -> io::Result<()> {
182 // Remove the `17: 0x0 - <unknown>` line.
183 if format == PrintFormat::Short && frame.exact_position == ptr::null() {
184 return Ok(());
185 }
186 match format {
187 PrintFormat::Full => write!(w,
188 " {:2}: {:2$?} - ",
189 idx,
190 frame.exact_position,
191 HEX_WIDTH)?,
192 PrintFormat::Short => write!(w, " {:2}: ", idx)?,
193 }
194 match s {
195 Some(string) => demangle(w, string, format)?,
196 None => w.write_all(b"<unknown>")?,
197 }
198 w.write_all(b"\n")
199 }
200
201 /// Print the filename and line number of the backtrace frame.
202 ///
203 /// See also `output`.
204 #[allow(dead_code)]
205 fn output_fileline(w: &mut Write,
206 file: &[u8],
207 line: u32,
208 format: PrintFormat) -> io::Result<()> {
209 // prior line: " ##: {:2$} - func"
210 w.write_all(b"")?;
211 match format {
212 PrintFormat::Full => write!(w,
213 " {:1$}",
214 "",
215 HEX_WIDTH)?,
216 PrintFormat::Short => write!(w, " ")?,
217 }
218
219 let file = str::from_utf8(file).unwrap_or("<unknown>");
220 let file_path = Path::new(file);
221 let mut already_printed = false;
222 if format == PrintFormat::Short && file_path.is_absolute() {
223 if let Ok(cwd) = env::current_dir() {
224 if let Ok(stripped) = file_path.strip_prefix(&cwd) {
225 if let Some(s) = stripped.to_str() {
226 write!(w, " at .{}{}:{}", path::MAIN_SEPARATOR, s, line)?;
227 already_printed = true;
228 }
229 }
230 }
231 }
232 if !already_printed {
233 write!(w, " at {}:{}", file, line)?;
234 }
235
236 w.write_all(b"\n")
237 }
238
239
240 // All rust symbols are in theory lists of "::"-separated identifiers. Some
241 // assemblers, however, can't handle these characters in symbol names. To get
242 // around this, we use C++-style mangling. The mangling method is:
243 //
244 // 1. Prefix the symbol with "_ZN"
245 // 2. For each element of the path, emit the length plus the element
246 // 3. End the path with "E"
247 //
248 // For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar".
249 //
250 // We're the ones printing our backtraces, so we can't rely on anything else to
251 // demangle our symbols. It's *much* nicer to look at demangled symbols, so
252 // this function is implemented to give us nice pretty output.
253 //
254 // Note that this demangler isn't quite as fancy as it could be. We have lots
255 // of other information in our symbols like hashes, version, type information,
256 // etc. Additionally, this doesn't handle glue symbols at all.
257 pub fn demangle(writer: &mut Write, mut s: &str, format: PrintFormat) -> io::Result<()> {
258 // During ThinLTO LLVM may import and rename internal symbols, so strip out
259 // those endings first as they're one of the last manglings applied to
260 // symbol names.
261 let llvm = ".llvm.";
262 if let Some(i) = s.find(llvm) {
263 let candidate = &s[i + llvm.len()..];
264 let all_hex = candidate.chars().all(|c| {
265 match c {
266 'A' ... 'F' | '0' ... '9' => true,
267 _ => false,
268 }
269 });
270
271 if all_hex {
272 s = &s[..i];
273 }
274 }
275
276 // Validate the symbol. If it doesn't look like anything we're
277 // expecting, we just print it literally. Note that we must handle non-rust
278 // symbols because we could have any function in the backtrace.
279 let mut valid = true;
280 let mut inner = s;
281 if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") {
282 inner = &s[3 .. s.len() - 1];
283 // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too.
284 } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") {
285 inner = &s[2 .. s.len() - 1];
286 } else {
287 valid = false;
288 }
289
290 if valid {
291 let mut chars = inner.chars();
292 while valid {
293 let mut i = 0;
294 for c in chars.by_ref() {
295 if c.is_numeric() {
296 i = i * 10 + c as usize - '0' as usize;
297 } else {
298 break
299 }
300 }
301 if i == 0 {
302 valid = chars.next().is_none();
303 break
304 } else if chars.by_ref().take(i - 1).count() != i - 1 {
305 valid = false;
306 }
307 }
308 }
309
310 // Alright, let's do this.
311 if !valid {
312 writer.write_all(s.as_bytes())?;
313 } else {
314 // remove the `::hfc2edb670e5eda97` part at the end of the symbol.
315 if format == PrintFormat::Short {
316 // The symbol in still mangled.
317 let mut split = inner.rsplitn(2, "17h");
318 match (split.next(), split.next()) {
319 (Some(addr), rest) => {
320 if addr.len() == 16 &&
321 addr.chars().all(|c| c.is_digit(16))
322 {
323 inner = rest.unwrap_or("");
324 }
325 }
326 _ => (),
327 }
328 }
329
330 let mut first = true;
331 while !inner.is_empty() {
332 if !first {
333 writer.write_all(b"::")?;
334 } else {
335 first = false;
336 }
337 let mut rest = inner;
338 while rest.chars().next().unwrap().is_numeric() {
339 rest = &rest[1..];
340 }
341 let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap();
342 inner = &rest[i..];
343 rest = &rest[..i];
344 if rest.starts_with("_$") {
345 rest = &rest[1..];
346 }
347 while !rest.is_empty() {
348 if rest.starts_with(".") {
349 if let Some('.') = rest[1..].chars().next() {
350 writer.write_all(b"::")?;
351 rest = &rest[2..];
352 } else {
353 writer.write_all(b".")?;
354 rest = &rest[1..];
355 }
356 } else if rest.starts_with("$") {
357 macro_rules! demangle {
358 ($($pat:expr => $demangled:expr),*) => ({
359 $(if rest.starts_with($pat) {
360 writer.write_all($demangled)?;
361 rest = &rest[$pat.len()..];
362 } else)*
363 {
364 writer.write_all(rest.as_bytes())?;
365 break;
366 }
367
368 })
369 }
370
371 // see src/librustc/back/link.rs for these mappings
372 demangle! (
373 "$SP$" => b"@",
374 "$BP$" => b"*",
375 "$RF$" => b"&",
376 "$LT$" => b"<",
377 "$GT$" => b">",
378 "$LP$" => b"(",
379 "$RP$" => b")",
380 "$C$" => b",",
381
382 // in theory we can demangle any Unicode code point, but
383 // for simplicity we just catch the common ones.
384 "$u7e$" => b"~",
385 "$u20$" => b" ",
386 "$u27$" => b"'",
387 "$u5b$" => b"[",
388 "$u5d$" => b"]",
389 "$u7b$" => b"{",
390 "$u7d$" => b"}",
391 "$u3b$" => b";",
392 "$u2b$" => b"+",
393 "$u22$" => b"\""
394 )
395 } else {
396 let idx = match rest.char_indices().find(|&(_, c)| c == '$' || c == '.') {
397 None => rest.len(),
398 Some((i, _)) => i,
399 };
400 writer.write_all(rest[..idx].as_bytes())?;
401 rest = &rest[idx..];
402 }
403 }
404 }
405 }
406
407 Ok(())
408 }
409
410 #[cfg(test)]
411 mod tests {
412 use sys_common;
413 macro_rules! t { ($a:expr, $b:expr) => ({
414 let mut m = Vec::new();
415 sys_common::backtrace::demangle(&mut m,
416 $a,
417 super::PrintFormat::Full).unwrap();
418 assert_eq!(String::from_utf8(m).unwrap(), $b);
419 }) }
420
421 #[test]
422 fn demangle() {
423 t!("test", "test");
424 t!("_ZN4testE", "test");
425 t!("_ZN4test", "_ZN4test");
426 t!("_ZN4test1a2bcE", "test::a::bc");
427 }
428
429 #[test]
430 fn demangle_dollars() {
431 t!("_ZN4$RP$E", ")");
432 t!("_ZN8$RF$testE", "&test");
433 t!("_ZN8$BP$test4foobE", "*test::foob");
434 t!("_ZN9$u20$test4foobE", " test::foob");
435 t!("_ZN35Bar$LT$$u5b$u32$u3b$$u20$4$u5d$$GT$E", "Bar<[u32; 4]>");
436 }
437
438 #[test]
439 fn demangle_many_dollars() {
440 t!("_ZN13test$u20$test4foobE", "test test::foob");
441 t!("_ZN12test$BP$test4foobE", "test*test::foob");
442 }
443
444 #[test]
445 fn demangle_windows() {
446 t!("ZN4testE", "test");
447 t!("ZN13test$u20$test4foobE", "test test::foob");
448 t!("ZN12test$RF$test4foobE", "test&test::foob");
449 }
450
451 #[test]
452 fn demangle_elements_beginning_with_underscore() {
453 t!("_ZN13_$LT$test$GT$E", "<test>");
454 t!("_ZN28_$u7b$$u7b$closure$u7d$$u7d$E", "{{closure}}");
455 t!("_ZN15__STATIC_FMTSTRE", "__STATIC_FMTSTR");
456 }
457
458 #[test]
459 fn demangle_trait_impls() {
460 t!("_ZN71_$LT$Test$u20$$u2b$$u20$$u27$static$u20$as$u20$foo..Bar$LT$Test$GT$$GT$3barE",
461 "<Test + 'static as foo::Bar<Test>>::bar");
462 }
463 }