]> git.proxmox.com Git - rustc.git/blob - src/libstd/sys/common/backtrace.rs
Imported Upstream version 1.9.0+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 #![cfg_attr(target_os = "nacl", allow(dead_code))]
12
13 use env;
14 use io::prelude::*;
15 use io;
16 use libc;
17 use str;
18 use sync::atomic::{self, Ordering};
19
20 pub use sys::backtrace::write;
21
22 #[cfg(target_pointer_width = "64")]
23 pub const HEX_WIDTH: usize = 18;
24
25 #[cfg(target_pointer_width = "32")]
26 pub const HEX_WIDTH: usize = 10;
27
28 // For now logging is turned off by default, and this function checks to see
29 // whether the magical environment variable is present to see if it's turned on.
30 pub fn log_enabled() -> bool {
31 static ENABLED: atomic::AtomicIsize = atomic::AtomicIsize::new(0);
32 match ENABLED.load(Ordering::SeqCst) {
33 1 => return false,
34 2 => return true,
35 _ => {}
36 }
37
38 let val = match env::var_os("RUST_BACKTRACE") {
39 Some(x) => if &x == "0" { 1 } else { 2 },
40 None => 1,
41 };
42 ENABLED.store(val, Ordering::SeqCst);
43 val == 2
44 }
45
46 // These output functions should now be used everywhere to ensure consistency.
47 pub fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void,
48 s: Option<&[u8]>) -> io::Result<()> {
49 write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)?;
50 match s.and_then(|s| str::from_utf8(s).ok()) {
51 Some(string) => demangle(w, string)?,
52 None => write!(w, "<unknown>")?,
53 }
54 w.write_all(&['\n' as u8])
55 }
56
57 #[allow(dead_code)]
58 pub fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int,
59 more: bool) -> io::Result<()> {
60 let file = str::from_utf8(file).unwrap_or("<unknown>");
61 // prior line: " ##: {:2$} - func"
62 write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH)?;
63 if more {
64 write!(w, " <... and possibly more>")?;
65 }
66 w.write_all(&['\n' as u8])
67 }
68
69
70 // All rust symbols are in theory lists of "::"-separated identifiers. Some
71 // assemblers, however, can't handle these characters in symbol names. To get
72 // around this, we use C++-style mangling. The mangling method is:
73 //
74 // 1. Prefix the symbol with "_ZN"
75 // 2. For each element of the path, emit the length plus the element
76 // 3. End the path with "E"
77 //
78 // For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar".
79 //
80 // We're the ones printing our backtraces, so we can't rely on anything else to
81 // demangle our symbols. It's *much* nicer to look at demangled symbols, so
82 // this function is implemented to give us nice pretty output.
83 //
84 // Note that this demangler isn't quite as fancy as it could be. We have lots
85 // of other information in our symbols like hashes, version, type information,
86 // etc. Additionally, this doesn't handle glue symbols at all.
87 pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> {
88 // First validate the symbol. If it doesn't look like anything we're
89 // expecting, we just print it literally. Note that we must handle non-rust
90 // symbols because we could have any function in the backtrace.
91 let mut valid = true;
92 let mut inner = s;
93 if s.len() > 4 && s.starts_with("_ZN") && s.ends_with("E") {
94 inner = &s[3 .. s.len() - 1];
95 // On Windows, dbghelp strips leading underscores, so we accept "ZN...E" form too.
96 } else if s.len() > 3 && s.starts_with("ZN") && s.ends_with("E") {
97 inner = &s[2 .. s.len() - 1];
98 } else {
99 valid = false;
100 }
101
102 if valid {
103 let mut chars = inner.chars();
104 while valid {
105 let mut i = 0;
106 for c in chars.by_ref() {
107 if c.is_numeric() {
108 i = i * 10 + c as usize - '0' as usize;
109 } else {
110 break
111 }
112 }
113 if i == 0 {
114 valid = chars.next().is_none();
115 break
116 } else if chars.by_ref().take(i - 1).count() != i - 1 {
117 valid = false;
118 }
119 }
120 }
121
122 // Alright, let's do this.
123 if !valid {
124 writer.write_all(s.as_bytes())?;
125 } else {
126 let mut first = true;
127 while !inner.is_empty() {
128 if !first {
129 writer.write_all(b"::")?;
130 } else {
131 first = false;
132 }
133 let mut rest = inner;
134 while rest.chars().next().unwrap().is_numeric() {
135 rest = &rest[1..];
136 }
137 let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap();
138 inner = &rest[i..];
139 rest = &rest[..i];
140 while !rest.is_empty() {
141 if rest.starts_with("$") {
142 macro_rules! demangle {
143 ($($pat:expr, => $demangled:expr),*) => ({
144 $(if rest.starts_with($pat) {
145 try!(writer.write_all($demangled));
146 rest = &rest[$pat.len()..];
147 } else)*
148 {
149 try!(writer.write_all(rest.as_bytes()));
150 break;
151 }
152
153 })
154 }
155
156 // see src/librustc/back/link.rs for these mappings
157 demangle! (
158 "$SP$", => b"@",
159 "$BP$", => b"*",
160 "$RF$", => b"&",
161 "$LT$", => b"<",
162 "$GT$", => b">",
163 "$LP$", => b"(",
164 "$RP$", => b")",
165 "$C$", => b",",
166
167 // in theory we can demangle any Unicode code point, but
168 // for simplicity we just catch the common ones.
169 "$u7e$", => b"~",
170 "$u20$", => b" ",
171 "$u27$", => b"'",
172 "$u5b$", => b"[",
173 "$u5d$", => b"]"
174 )
175 } else {
176 let idx = match rest.find('$') {
177 None => rest.len(),
178 Some(i) => i,
179 };
180 writer.write_all(rest[..idx].as_bytes())?;
181 rest = &rest[idx..];
182 }
183 }
184 }
185 }
186
187 Ok(())
188 }
189
190 #[cfg(test)]
191 mod tests {
192 use prelude::v1::*;
193 use sys_common;
194 macro_rules! t { ($a:expr, $b:expr) => ({
195 let mut m = Vec::new();
196 sys_common::backtrace::demangle(&mut m, $a).unwrap();
197 assert_eq!(String::from_utf8(m).unwrap(), $b);
198 }) }
199
200 #[test]
201 fn demangle() {
202 t!("test", "test");
203 t!("_ZN4testE", "test");
204 t!("_ZN4test", "_ZN4test");
205 t!("_ZN4test1a2bcE", "test::a::bc");
206 }
207
208 #[test]
209 fn demangle_dollars() {
210 t!("_ZN4$RP$E", ")");
211 t!("_ZN8$RF$testE", "&test");
212 t!("_ZN8$BP$test4foobE", "*test::foob");
213 t!("_ZN9$u20$test4foobE", " test::foob");
214 }
215
216 #[test]
217 fn demangle_many_dollars() {
218 t!("_ZN13test$u20$test4foobE", "test test::foob");
219 t!("_ZN12test$BP$test4foobE", "test*test::foob");
220 }
221
222 #[test]
223 fn demangle_windows() {
224 t!("ZN4testE", "test");
225 t!("ZN13test$u20$test4foobE", "test test::foob");
226 t!("ZN12test$RF$test4foobE", "test&test::foob");
227 }
228 }