]>
Commit | Line | Data |
---|---|---|
ea8adc8c XL |
1 | use addr2line; |
2 | use findshlibs::{self, Segment, SharedLibrary}; | |
83c7162d XL |
3 | use gimli; |
4 | use memmap::Mmap; | |
5 | use object::{self, Object}; | |
ea8adc8c XL |
6 | use std::cell::RefCell; |
7 | use std::env; | |
83c7162d XL |
8 | use std::fs::File; |
9 | use std::mem; | |
0731742a XL |
10 | use libc::c_void; |
11 | use std::path::PathBuf; | |
ea8adc8c | 12 | use std::u32; |
0731742a | 13 | use std::prelude::v1::*; |
ea8adc8c XL |
14 | |
15 | use SymbolName; | |
0731742a | 16 | use types::BytesOrWideString; |
ea8adc8c XL |
17 | |
18 | const MAPPINGS_CACHE_SIZE: usize = 4; | |
19 | ||
0731742a | 20 | type Dwarf = addr2line::Context<gimli::EndianRcSlice<gimli::RunTimeEndian>>; |
83c7162d XL |
21 | type Symbols<'map> = object::SymbolMap<'map>; |
22 | ||
23 | struct 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 | ||
30 | impl 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 |
58 | thread_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 | 73 | fn with_mapping_for_path<F>(path: PathBuf, f: F) |
ea8adc8c | 74 | where |
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 | ||
111 | pub 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 | ||
185 | pub struct Symbol { | |
186 | addr: usize, | |
0731742a | 187 | file: Option<String>, |
ea8adc8c XL |
188 | line: Option<u64>, |
189 | name: Option<String>, | |
190 | } | |
191 | ||
192 | impl 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 | } |