]>
Commit | Line | Data |
---|---|---|
1a4d82fc JJ |
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 | ||
92a42be0 SL |
11 | #![cfg_attr(target_os = "nacl", allow(dead_code))] |
12 | ||
e9174d1e | 13 | use env; |
c34b1796 | 14 | use io::prelude::*; |
c34b1796 | 15 | use io; |
e9174d1e SL |
16 | use libc; |
17 | use str; | |
18 | use sync::atomic::{self, Ordering}; | |
19 | ||
20 | pub use sys::backtrace::write; | |
1a4d82fc | 21 | |
85aaf69f | 22 | #[cfg(target_pointer_width = "64")] |
c34b1796 | 23 | pub const HEX_WIDTH: usize = 18; |
1a4d82fc | 24 | |
85aaf69f | 25 | #[cfg(target_pointer_width = "32")] |
c34b1796 | 26 | pub const HEX_WIDTH: usize = 10; |
1a4d82fc | 27 | |
e9174d1e SL |
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") { | |
54a0048b | 39 | Some(x) => if &x == "0" { 1 } else { 2 }, |
e9174d1e SL |
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<()> { | |
54a0048b | 49 | write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)?; |
e9174d1e | 50 | match s.and_then(|s| str::from_utf8(s).ok()) { |
54a0048b SL |
51 | Some(string) => demangle(w, string)?, |
52 | None => write!(w, "<unknown>")?, | |
e9174d1e SL |
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" | |
54a0048b | 62 | write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH)?; |
e9174d1e | 63 | if more { |
54a0048b | 64 | write!(w, " <... and possibly more>")?; |
e9174d1e SL |
65 | } |
66 | w.write_all(&['\n' as u8]) | |
67 | } | |
68 | ||
69 | ||
1a4d82fc JJ |
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 | // | |
62682a34 | 78 | // For example, "_ZN4testE" => "test" and "_ZN3foo3barE" => "foo::bar". |
1a4d82fc JJ |
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. | |
c34b1796 | 87 | pub fn demangle(writer: &mut Write, s: &str) -> io::Result<()> { |
1a4d82fc JJ |
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") { | |
85aaf69f | 94 | inner = &s[3 .. s.len() - 1]; |
1a4d82fc JJ |
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") { | |
85aaf69f | 97 | inner = &s[2 .. s.len() - 1]; |
1a4d82fc JJ |
98 | } else { |
99 | valid = false; | |
100 | } | |
101 | ||
102 | if valid { | |
103 | let mut chars = inner.chars(); | |
104 | while valid { | |
105 | let mut i = 0; | |
85aaf69f | 106 | for c in chars.by_ref() { |
1a4d82fc | 107 | if c.is_numeric() { |
c34b1796 | 108 | i = i * 10 + c as usize - '0' as usize; |
1a4d82fc JJ |
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 { | |
54a0048b | 124 | writer.write_all(s.as_bytes())?; |
1a4d82fc JJ |
125 | } else { |
126 | let mut first = true; | |
9346a6ac | 127 | while !inner.is_empty() { |
1a4d82fc | 128 | if !first { |
54a0048b | 129 | writer.write_all(b"::")?; |
1a4d82fc JJ |
130 | } else { |
131 | first = false; | |
132 | } | |
133 | let mut rest = inner; | |
54a0048b | 134 | while rest.chars().next().unwrap().is_numeric() { |
85aaf69f | 135 | rest = &rest[1..]; |
1a4d82fc | 136 | } |
c34b1796 | 137 | let i: usize = inner[.. (inner.len() - rest.len())].parse().unwrap(); |
85aaf69f SL |
138 | inner = &rest[i..]; |
139 | rest = &rest[..i]; | |
9346a6ac | 140 | while !rest.is_empty() { |
1a4d82fc JJ |
141 | if rest.starts_with("$") { |
142 | macro_rules! demangle { | |
143 | ($($pat:expr, => $demangled:expr),*) => ({ | |
144 | $(if rest.starts_with($pat) { | |
c34b1796 | 145 | try!(writer.write_all($demangled)); |
85aaf69f | 146 | rest = &rest[$pat.len()..]; |
1a4d82fc JJ |
147 | } else)* |
148 | { | |
c34b1796 | 149 | try!(writer.write_all(rest.as_bytes())); |
1a4d82fc JJ |
150 | break; |
151 | } | |
152 | ||
153 | }) | |
154 | } | |
155 | ||
156 | // see src/librustc/back/link.rs for these mappings | |
157 | demangle! ( | |
c34b1796 AL |
158 | "$SP$", => b"@", |
159 | "$BP$", => b"*", | |
160 | "$RF$", => b"&", | |
161 | "$LT$", => b"<", | |
162 | "$GT$", => b">", | |
163 | "$LP$", => b"(", | |
164 | "$RP$", => b")", | |
165 | "$C$", => b",", | |
1a4d82fc JJ |
166 | |
167 | // in theory we can demangle any Unicode code point, but | |
168 | // for simplicity we just catch the common ones. | |
c34b1796 AL |
169 | "$u7e$", => b"~", |
170 | "$u20$", => b" ", | |
171 | "$u27$", => b"'", | |
172 | "$u5b$", => b"[", | |
173 | "$u5d$", => b"]" | |
1a4d82fc JJ |
174 | ) |
175 | } else { | |
176 | let idx = match rest.find('$') { | |
177 | None => rest.len(), | |
178 | Some(i) => i, | |
179 | }; | |
54a0048b | 180 | writer.write_all(rest[..idx].as_bytes())?; |
85aaf69f | 181 | rest = &rest[idx..]; |
1a4d82fc JJ |
182 | } |
183 | } | |
184 | } | |
185 | } | |
186 | ||
187 | Ok(()) | |
188 | } | |
e9174d1e SL |
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 | } |