]> git.proxmox.com Git - rustc.git/blame - src/libstd/sys/common/backtrace.rs
Imported Upstream version 1.9.0+dfsg1
[rustc.git] / src / libstd / sys / common / backtrace.rs
CommitLineData
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 13use env;
c34b1796 14use io::prelude::*;
c34b1796 15use io;
e9174d1e
SL
16use libc;
17use str;
18use sync::atomic::{self, Ordering};
19
20pub use sys::backtrace::write;
1a4d82fc 21
85aaf69f 22#[cfg(target_pointer_width = "64")]
c34b1796 23pub const HEX_WIDTH: usize = 18;
1a4d82fc 24
85aaf69f 25#[cfg(target_pointer_width = "32")]
c34b1796 26pub 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.
30pub 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.
47pub 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)]
58pub 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 87pub 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)]
191mod 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}