]>
Commit | Line | Data |
---|---|---|
dc9dc135 XL |
1 | //! Symbolication strategy using `dbghelp.dll` on Windows, only used for MSVC |
2 | //! | |
3 | //! This symbolication strategy, like with backtraces, uses dynamically loaded | |
4 | //! information from `dbghelp.dll`. (see `src/dbghelp.rs` for info about why | |
5 | //! it's dynamically loaded). | |
6 | //! | |
7 | //! This API selects its resolution strategy based on the frame provided or the | |
8 | //! information we have at hand. If a frame from `StackWalkEx` is given to us | |
9 | //! then we use similar APIs to generate correct information about inlined | |
10 | //! functions. Otherwise if all we have is an address or an older stack frame | |
11 | //! from `StackWalk64` we use the older APIs for symbolication. | |
12 | //! | |
13 | //! There's a good deal of support in this module, but a good chunk of it is | |
14 | //! converting back and forth between Windows types and Rust types. For example | |
15 | //! symbols come to us as wide strings which we then convert to utf-8 strings if | |
16 | //! we can. | |
17 | ||
7cac9316 XL |
18 | #![allow(bad_style)] |
19 | ||
ed00b5ec | 20 | use super::super::{dbghelp, windows::*}; |
3dfed10e | 21 | use super::{BytesOrWideString, ResolveWhat, SymbolName}; |
0731742a | 22 | use core::char; |
416331ca XL |
23 | use core::ffi::c_void; |
24 | use core::marker; | |
dc9dc135 XL |
25 | use core::mem; |
26 | use core::slice; | |
7cac9316 | 27 | |
0731742a | 28 | // Store an OsString on std so we can provide the symbol name and filename. |
416331ca | 29 | pub struct Symbol<'a> { |
0731742a | 30 | name: *const [u8], |
7cac9316 XL |
31 | addr: *mut c_void, |
32 | line: Option<u32>, | |
0731742a XL |
33 | filename: Option<*const [u16]>, |
34 | #[cfg(feature = "std")] | |
35 | _filename_cache: Option<::std::ffi::OsString>, | |
36 | #[cfg(not(feature = "std"))] | |
37 | _filename_cache: (), | |
416331ca | 38 | _marker: marker::PhantomData<&'a i32>, |
7cac9316 XL |
39 | } |
40 | ||
416331ca | 41 | impl Symbol<'_> { |
f035d41b | 42 | pub fn name(&self) -> Option<SymbolName<'_>> { |
0731742a | 43 | Some(SymbolName::new(unsafe { &*self.name })) |
7cac9316 XL |
44 | } |
45 | ||
46 | pub fn addr(&self) -> Option<*mut c_void> { | |
47 | Some(self.addr as *mut _) | |
48 | } | |
49 | ||
f035d41b | 50 | pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> { |
dc9dc135 XL |
51 | self.filename |
52 | .map(|slice| unsafe { BytesOrWideString::Wide(&*slice) }) | |
7cac9316 XL |
53 | } |
54 | ||
29967ef6 XL |
55 | pub fn colno(&self) -> Option<u32> { |
56 | None | |
57 | } | |
58 | ||
7cac9316 XL |
59 | pub fn lineno(&self) -> Option<u32> { |
60 | self.line | |
61 | } | |
0731742a XL |
62 | |
63 | #[cfg(feature = "std")] | |
dc9dc135 XL |
64 | pub fn filename(&self) -> Option<&::std::path::Path> { |
65 | use std::path::Path; | |
66 | ||
67 | self._filename_cache.as_ref().map(Path::new) | |
0731742a | 68 | } |
7cac9316 XL |
69 | } |
70 | ||
0731742a XL |
71 | #[repr(C, align(8))] |
72 | struct Aligned8<T>(T); | |
73 | ||
f035d41b | 74 | pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) { |
dc9dc135 XL |
75 | // Ensure this process's symbols are initialized |
76 | let dbghelp = match dbghelp::init() { | |
77 | Ok(dbghelp) => dbghelp, | |
78 | Err(()) => return, // oh well... | |
79 | }; | |
80 | ||
ed00b5ec FG |
81 | let resolve_inner = if (*dbghelp.dbghelp()).SymAddrIncludeInlineTrace().is_some() { |
82 | // We are on a version of dbghelp 6.2+, which contains the more modern | |
83 | // Inline APIs. | |
84 | resolve_with_inline | |
85 | } else { | |
86 | // We are on an older version of dbghelp which doesn't contain the Inline | |
87 | // APIs. | |
88 | resolve_legacy | |
89 | }; | |
dc9dc135 | 90 | match what { |
ed00b5ec FG |
91 | ResolveWhat::Address(_) => resolve_inner(&dbghelp, what.address_or_ip(), None, cb), |
92 | ResolveWhat::Frame(frame) => { | |
93 | resolve_inner(&dbghelp, frame.ip(), frame.inner.inline_context(), cb) | |
94 | } | |
dc9dc135 XL |
95 | } |
96 | } | |
97 | ||
ed00b5ec FG |
98 | /// Resolve the address using the legacy dbghelp API. |
99 | /// | |
100 | /// This should work all the way down to Windows XP. The inline context is | |
101 | /// ignored, since this concept was only introduced in dbghelp 6.2+. | |
102 | unsafe fn resolve_legacy( | |
416331ca | 103 | dbghelp: &dbghelp::Init, |
ed00b5ec FG |
104 | addr: *mut c_void, |
105 | _inline_context: Option<DWORD>, | |
f035d41b | 106 | cb: &mut dyn FnMut(&super::Symbol), |
dc9dc135 | 107 | ) { |
ed00b5ec | 108 | let addr = super::adjust_ip(addr) as DWORD64; |
dc9dc135 | 109 | do_resolve( |
ed00b5ec FG |
110 | |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr, &mut 0, info), |
111 | |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr, &mut 0, line), | |
dc9dc135 XL |
112 | cb, |
113 | ) | |
114 | } | |
115 | ||
ed00b5ec FG |
116 | /// Resolve the address using the modern dbghelp APIs. |
117 | /// | |
118 | /// Note that calling this function requires having dbghelp 6.2+ loaded - and | |
119 | /// will panic otherwise. | |
120 | unsafe fn resolve_with_inline( | |
416331ca | 121 | dbghelp: &dbghelp::Init, |
dc9dc135 | 122 | addr: *mut c_void, |
ed00b5ec | 123 | inline_context: Option<DWORD>, |
f035d41b | 124 | cb: &mut dyn FnMut(&super::Symbol), |
dc9dc135 | 125 | ) { |
ed00b5ec FG |
126 | let current_process = GetCurrentProcess(); |
127 | ||
128 | let addr = super::adjust_ip(addr) as DWORD64; | |
129 | ||
130 | let (inlined_frame_count, inline_context) = if let Some(ic) = inline_context { | |
131 | (0, ic) | |
132 | } else { | |
133 | let mut inlined_frame_count = dbghelp.SymAddrIncludeInlineTrace()(current_process, addr); | |
134 | ||
135 | let mut inline_context = 0; | |
136 | ||
137 | // If there is are inlined frames but we can't load them for some reason OR if there are no | |
138 | // inlined frames, then we disregard inlined_frame_count and inline_context. | |
139 | if (inlined_frame_count > 0 | |
140 | && dbghelp.SymQueryInlineTrace()( | |
141 | current_process, | |
142 | addr, | |
143 | 0, | |
144 | addr, | |
145 | addr, | |
146 | &mut inline_context, | |
147 | &mut 0, | |
148 | ) != TRUE) | |
149 | || inlined_frame_count == 0 | |
150 | { | |
151 | inlined_frame_count = 0; | |
152 | inline_context = 0; | |
153 | } | |
154 | ||
155 | (inlined_frame_count, inline_context) | |
156 | }; | |
157 | ||
158 | let last_inline_context = inline_context + 1 + inlined_frame_count; | |
159 | ||
160 | for inline_context in inline_context..last_inline_context { | |
161 | do_resolve( | |
162 | |info| { | |
163 | dbghelp.SymFromInlineContextW()(current_process, addr, inline_context, &mut 0, info) | |
164 | }, | |
165 | |line| { | |
166 | dbghelp.SymGetLineFromInlineContextW()( | |
167 | current_process, | |
168 | addr, | |
169 | inline_context, | |
170 | 0, | |
171 | &mut 0, | |
172 | line, | |
173 | ) | |
174 | }, | |
175 | cb, | |
176 | ); | |
177 | } | |
dc9dc135 XL |
178 | } |
179 | ||
180 | unsafe fn do_resolve( | |
181 | sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL, | |
182 | get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL, | |
f035d41b | 183 | cb: &mut dyn FnMut(&super::Symbol), |
dc9dc135 | 184 | ) { |
0731742a XL |
185 | const SIZE: usize = 2 * MAX_SYM_NAME + mem::size_of::<SYMBOL_INFOW>(); |
186 | let mut data = Aligned8([0u8; SIZE]); | |
187 | let data = &mut data.0; | |
188 | let info = &mut *(data.as_mut_ptr() as *mut SYMBOL_INFOW); | |
189 | info.MaxNameLen = MAX_SYM_NAME as ULONG; | |
190 | // the struct size in C. the value is different to | |
191 | // `size_of::<SYMBOL_INFOW>() - MAX_SYM_NAME + 1` (== 81) | |
192 | // due to struct alignment. | |
193 | info.SizeOfStruct = 88; | |
194 | ||
dc9dc135 XL |
195 | if sym_from_addr(info) != TRUE { |
196 | return; | |
0731742a | 197 | } |
ea8adc8c | 198 | |
0731742a XL |
199 | // If the symbol name is greater than MaxNameLen, SymFromAddrW will |
200 | // give a buffer of (MaxNameLen - 1) characters and set NameLen to | |
201 | // the real value. | |
dc9dc135 | 202 | let name_len = ::core::cmp::min(info.NameLen as usize, info.MaxNameLen as usize - 1); |
0731742a XL |
203 | let name_ptr = info.Name.as_ptr() as *const u16; |
204 | let name = slice::from_raw_parts(name_ptr, name_len); | |
205 | ||
206 | // Reencode the utf-16 symbol to utf-8 so we can use `SymbolName::new` like | |
207 | // all other platforms | |
208 | let mut name_len = 0; | |
209 | let mut name_buffer = [0; 256]; | |
210 | { | |
211 | let mut remaining = &mut name_buffer[..]; | |
212 | for c in char::decode_utf16(name.iter().cloned()) { | |
213 | let c = c.unwrap_or(char::REPLACEMENT_CHARACTER); | |
214 | let len = c.len_utf8(); | |
215 | if len < remaining.len() { | |
216 | c.encode_utf8(remaining); | |
217 | let tmp = remaining; | |
218 | remaining = &mut tmp[len..]; | |
219 | name_len += len; | |
220 | } else { | |
dc9dc135 | 221 | break; |
7cac9316 | 222 | } |
0731742a XL |
223 | } |
224 | } | |
225 | let name = &name_buffer[..name_len] as *const [u8]; | |
226 | ||
227 | let mut line = mem::zeroed::<IMAGEHLP_LINEW64>(); | |
228 | line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as DWORD; | |
0731742a XL |
229 | |
230 | let mut filename = None; | |
231 | let mut lineno = None; | |
dc9dc135 | 232 | if get_line_from_addr(&mut line) == TRUE { |
0731742a XL |
233 | lineno = Some(line.LineNumber as u32); |
234 | ||
235 | let base = line.FileName; | |
236 | let mut len = 0; | |
237 | while *base.offset(len) != 0 { | |
238 | len += 1; | |
7cac9316 XL |
239 | } |
240 | ||
0731742a XL |
241 | let len = len as usize; |
242 | ||
243 | filename = Some(slice::from_raw_parts(base, len) as *const [u16]); | |
7cac9316 | 244 | } |
0731742a | 245 | |
0731742a XL |
246 | cb(&super::Symbol { |
247 | inner: Symbol { | |
248 | name, | |
249 | addr: info.Address as *mut _, | |
250 | line: lineno, | |
251 | filename, | |
252 | _filename_cache: cache(filename), | |
416331ca | 253 | _marker: marker::PhantomData, |
0731742a XL |
254 | }, |
255 | }) | |
256 | } | |
257 | ||
258 | #[cfg(feature = "std")] | |
259 | unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> { | |
260 | use std::os::windows::ffi::OsStringExt; | |
dc9dc135 | 261 | filename.map(|f| ::std::ffi::OsString::from_wide(&*f)) |
0731742a XL |
262 | } |
263 | ||
264 | #[cfg(not(feature = "std"))] | |
dc9dc135 | 265 | unsafe fn cache(_filename: Option<*const [u16]>) {} |
fc512014 XL |
266 | |
267 | pub unsafe fn clear_symbol_cache() {} |