]> git.proxmox.com Git - rustc.git/blame - vendor/backtrace/src/symbolize/gimli.rs
New upstream version 1.33.0+dfsg1
[rustc.git] / vendor / backtrace / src / symbolize / gimli.rs
CommitLineData
ea8adc8c
XL
1use addr2line;
2use findshlibs::{self, Segment, SharedLibrary};
83c7162d
XL
3use gimli;
4use memmap::Mmap;
5use object::{self, Object};
ea8adc8c
XL
6use std::cell::RefCell;
7use std::env;
83c7162d
XL
8use std::fs::File;
9use std::mem;
0731742a
XL
10use libc::c_void;
11use std::path::PathBuf;
ea8adc8c 12use std::u32;
0731742a 13use std::prelude::v1::*;
ea8adc8c
XL
14
15use SymbolName;
0731742a 16use types::BytesOrWideString;
ea8adc8c
XL
17
18const MAPPINGS_CACHE_SIZE: usize = 4;
19
0731742a 20type Dwarf = addr2line::Context<gimli::EndianRcSlice<gimli::RunTimeEndian>>;
83c7162d
XL
21type Symbols<'map> = object::SymbolMap<'map>;
22
23struct Mapping {
0731742a 24 dwarf: Dwarf,
83c7162d 25 // 'static lifetime is a lie to hack around lack of support for self-referential structs.
83c7162d
XL
26 symbols: Symbols<'static>,
27 _map: Mmap,
28}
29
30impl Mapping {
31 fn new(path: &PathBuf) -> Option<Mapping> {
32 let file = File::open(path).ok()?;
33 // TODO: not completely safe, see https://github.com/danburkert/memmap-rs/issues/25
34 let map = unsafe { Mmap::map(&file).ok()? };
35 let (dwarf, symbols) = {
36 let object = object::File::parse(&*map).ok()?;
37 let dwarf = addr2line::Context::new(&object).ok()?;
38 let symbols = object.symbol_map();
39 // Convert to 'static lifetimes.
0731742a 40 (dwarf, unsafe { mem::transmute(symbols) })
83c7162d
XL
41 };
42 Some(Mapping {
43 dwarf,
44 symbols,
45 _map: map,
46 })
47 }
48
49 // Ensure the 'static lifetimes don't leak.
50 fn rent<F>(&self, mut f: F)
51 where
52 F: FnMut(&Dwarf, &Symbols),
53 {
54 f(&self.dwarf, &self.symbols)
55 }
56}
57
ea8adc8c
XL
58thread_local! {
59 // A very small, very simple LRU cache for debug info mappings.
60 //
61 // The hit rate should be very high, since the typical stack doesn't cross
62 // between many shared libraries.
63 //
83c7162d 64 // The `addr2line::Context` structures are pretty expensive to create. Its
ea8adc8c 65 // cost is expected to be amortized by subsequent `locate` queries, which
83c7162d 66 // leverage the structures built when constructing `addr2line::Context`s to
ea8adc8c
XL
67 // get nice speedups. If we didn't have this cache, that amortization would
68 // never happen, and symbolicating backtraces would be ssssllllooooowwww.
83c7162d 69 static MAPPINGS_CACHE: RefCell<Vec<(PathBuf, Mapping)>>
ea8adc8c
XL
70 = RefCell::new(Vec::with_capacity(MAPPINGS_CACHE_SIZE));
71}
72
83c7162d 73fn with_mapping_for_path<F>(path: PathBuf, f: F)
ea8adc8c 74where
83c7162d 75 F: FnMut(&Dwarf, &Symbols),
ea8adc8c
XL
76{
77 MAPPINGS_CACHE.with(|cache| {
78 let mut cache = cache.borrow_mut();
79
80 let idx = cache.iter().position(|&(ref p, _)| p == &path);
81
82 // Invariant: after this conditional completes without early returning
83 // from an error, the cache entry for this path is at index 0.
84
85 if let Some(idx) = idx {
86 // When the mapping is already in the cache, move it to the front.
87 if idx != 0 {
88 let entry = cache.remove(idx);
89 cache.insert(0, entry);
90 }
91 } else {
92 // When the mapping is not in the cache, create a new mapping,
93 // insert it into the front of the cache, and evict the oldest cache
94 // entry if necessary.
83c7162d
XL
95 let mapping = match Mapping::new(&path) {
96 None => return,
97 Some(m) => m,
ea8adc8c
XL
98 };
99
100 if cache.len() == MAPPINGS_CACHE_SIZE {
101 cache.pop();
102 }
103
104 cache.insert(0, (path, mapping));
105 }
106
83c7162d 107 cache[0].1.rent(f);
ea8adc8c
XL
108 });
109}
110
111pub fn resolve(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) {
112 // First, find the file containing the segment that the given AVMA (after
113 // relocation) address falls within. Use the containing segment to compute
114 // the SVMA (before relocation) address.
115 //
116 // Note that the OS APIs that `SharedLibrary::each` is implemented with hold
117 // a lock for the duration of the `each` call, so we want to keep this
118 // section as short as possible to avoid contention with other threads
119 // capturing backtraces.
120 let addr = findshlibs::Avma(addr as *mut u8 as *const u8);
121 let mut so_info = None;
122 findshlibs::TargetSharedLibrary::each(|so| {
123 use findshlibs::IterationControl::*;
124
125 for segment in so.segments() {
126 if segment.contains_avma(so, addr) {
127 let addr = so.avma_to_svma(addr);
128 let path = so.name().to_string_lossy();
129 so_info = Some((addr, path.to_string()));
130 return Break;
131 }
132 }
133
134 Continue
135 });
136 let (addr, path) = match so_info {
137 None => return,
138 Some((a, p)) => (a, p),
139 };
140
141 // Second, fixup the path. Empty path means that this address falls within
142 // the main executable, not a shared library.
143 let path = if path.is_empty() {
144 match env::current_exe() {
145 Err(_) => return,
146 Ok(p) => p,
147 }
148 } else {
149 PathBuf::from(path)
150 };
151
152 // Finally, get a cached mapping or create a new mapping for this file, and
153 // evaluate the DWARF info to find the file/line/name for this address.
83c7162d
XL
154 with_mapping_for_path(path, |dwarf, symbols| {
155 let mut found_sym = false;
156 if let Ok(mut frames) = dwarf.find_frames(addr.0 as u64) {
157 while let Ok(Some(frame)) = frames.next() {
158 let (file, line) = frame
159 .location
160 .map(|l| (l.file, l.line))
161 .unwrap_or((None, None));
162 let name = frame
163 .function
164 .and_then(|f| f.raw_name().ok().map(|f| f.to_string()));
165 let sym = super::Symbol {
166 inner: Symbol::new(addr.0 as usize, file, line, name),
167 };
168 cb(&sym);
169 found_sym = true;
170 }
171 }
ea8adc8c 172
83c7162d
XL
173 // No DWARF info found, so fallback to the symbol table.
174 if !found_sym {
175 if let Some(name) = symbols.get(addr.0 as u64).and_then(|x| x.name()) {
176 let sym = super::Symbol {
177 inner: Symbol::new(addr.0 as usize, None, None, Some(name.to_string())),
178 };
179 cb(&sym);
180 }
181 }
ea8adc8c
XL
182 });
183}
184
185pub struct Symbol {
186 addr: usize,
0731742a 187 file: Option<String>,
ea8adc8c
XL
188 line: Option<u64>,
189 name: Option<String>,
190}
191
192impl Symbol {
193 fn new(addr: usize,
0731742a 194 file: Option<String>,
ea8adc8c
XL
195 line: Option<u64>,
196 name: Option<String>)
197 -> Symbol {
198 Symbol {
199 addr,
200 file,
201 line,
202 name,
203 }
204 }
205
206 pub fn name(&self) -> Option<SymbolName> {
207 self.name.as_ref().map(|s| SymbolName::new(s.as_bytes()))
208 }
209
210 pub fn addr(&self) -> Option<*mut c_void> {
211 Some(self.addr as *mut c_void)
212 }
213
0731742a
XL
214 pub fn filename_raw(&self) -> Option<BytesOrWideString> {
215 self.file.as_ref().map(|f| BytesOrWideString::Bytes(f.as_bytes()))
ea8adc8c
XL
216 }
217
218 pub fn lineno(&self) -> Option<u32> {
219 self.line
220 .and_then(|l| if l > (u32::MAX as u64) {
221 None
222 } else {
223 Some(l as u32)
224 })
225 }
226}