1#![allow(bad_style)]
19
20use super::super::{dbghelp, windows_sys::*};
21use super::{BytesOrWideString, ResolveWhat, SymbolName};
22use core::cmp;
23use core::ffi::c_void;
24use core::marker;
25use core::mem::{self, MaybeUninit};
26use core::ptr;
27use core::slice;
28
29#[inline(always)]
31#[must_use]
32const fn ptr_from_ref<T: ?Sized>(r: &T) -> *const T {
33 r
34}
35
36pub struct Symbol<'a> {
38 name: *const [u8],
39 addr: *mut c_void,
40 line: Option<u32>,
41 filename: Option<*const [u16]>,
42 #[cfg(feature = "std")]
43 _filename_cache: Option<::std::ffi::OsString>,
44 #[cfg(not(feature = "std"))]
45 _filename_cache: (),
46 _marker: marker::PhantomData<&'a i32>,
47}
48
49impl Symbol<'_> {
50 pub fn name(&self) -> Option<SymbolName<'_>> {
51 Some(SymbolName::new(unsafe { &*self.name }))
52 }
53
54 pub fn addr(&self) -> Option<*mut c_void> {
55 Some(self.addr)
56 }
57
58 pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
59 self.filename
60 .map(|slice| unsafe { BytesOrWideString::Wide(&*slice) })
61 }
62
63 pub fn colno(&self) -> Option<u32> {
64 None
65 }
66
67 pub fn lineno(&self) -> Option<u32> {
68 self.line
69 }
70
71 #[cfg(feature = "std")]
72 pub fn filename(&self) -> Option<&::std::path::Path> {
73 use std::path::Path;
74
75 self._filename_cache.as_ref().map(Path::new)
76 }
77}
78
79#[repr(C, align(8))]
80struct Aligned8<T>(T);
81
82#[cfg(not(target_vendor = "win7"))]
83pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
84 let dbghelp = match dbghelp::init() {
86 Ok(dbghelp) => dbghelp,
87 Err(()) => return, };
89 unsafe {
90 match what {
91 ResolveWhat::Address(_) => {
92 resolve_with_inline(&dbghelp, what.address_or_ip(), None, cb)
93 }
94 ResolveWhat::Frame(frame) => {
95 resolve_with_inline(&dbghelp, frame.ip(), frame.inner.inline_context(), cb)
96 }
97 };
98 }
99}
100
101#[cfg(target_vendor = "win7")]
102pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
103 let dbghelp = match dbghelp::init() {
105 Ok(dbghelp) => dbghelp,
106 Err(()) => return, };
108
109 unsafe {
110 let resolve_inner = if (*dbghelp.dbghelp()).SymAddrIncludeInlineTrace().is_some() {
111 resolve_with_inline
114 } else {
115 resolve_legacy
118 };
119 match what {
120 ResolveWhat::Address(_) => resolve_inner(&dbghelp, what.address_or_ip(), None, cb),
121 ResolveWhat::Frame(frame) => {
122 resolve_inner(&dbghelp, frame.ip(), frame.inner.inline_context(), cb)
123 }
124 };
125 }
126}
127
128#[cfg(target_vendor = "win7")]
133unsafe fn resolve_legacy(
134 dbghelp: &dbghelp::Init,
135 addr: *mut c_void,
136 _inline_context: Option<u32>,
137 cb: &mut dyn FnMut(&super::Symbol),
138) -> Option<()> {
139 let addr = super::adjust_ip(addr) as u64;
140 unsafe {
141 do_resolve(
142 |info| dbghelp.SymFromAddrW()(GetCurrentProcess(), addr, &mut 0, info),
143 |line| dbghelp.SymGetLineFromAddrW64()(GetCurrentProcess(), addr, &mut 0, line),
144 cb,
145 );
146 }
147 Some(())
148}
149
150unsafe fn resolve_with_inline(
155 dbghelp: &dbghelp::Init,
156 addr: *mut c_void,
157 inline_context: Option<u32>,
158 cb: &mut dyn FnMut(&super::Symbol),
159) -> Option<()> {
160 unsafe {
161 let current_process = GetCurrentProcess();
162 let SymFromInlineContextW = (*dbghelp.dbghelp()).SymFromInlineContextW()?;
164 let SymGetLineFromInlineContextW = (*dbghelp.dbghelp()).SymGetLineFromInlineContextW()?;
165
166 let addr = super::adjust_ip(addr) as u64;
167
168 let (inlined_frame_count, inline_context) = if let Some(ic) = inline_context {
169 (0, ic)
170 } else {
171 let SymAddrIncludeInlineTrace = (*dbghelp.dbghelp()).SymAddrIncludeInlineTrace()?;
172 let SymQueryInlineTrace = (*dbghelp.dbghelp()).SymQueryInlineTrace()?;
173
174 let mut inlined_frame_count = SymAddrIncludeInlineTrace(current_process, addr);
175
176 let mut inline_context = 0;
177
178 if (inlined_frame_count > 0
181 && SymQueryInlineTrace(
182 current_process,
183 addr,
184 0,
185 addr,
186 addr,
187 &mut inline_context,
188 &mut 0,
189 ) != TRUE)
190 || inlined_frame_count == 0
191 {
192 inlined_frame_count = 0;
193 inline_context = 0;
194 }
195
196 (inlined_frame_count, inline_context)
197 };
198
199 let last_inline_context = inline_context + 1 + inlined_frame_count;
200
201 for inline_context in inline_context..last_inline_context {
202 do_resolve(
203 |info| SymFromInlineContextW(current_process, addr, inline_context, &mut 0, info),
204 |line| {
205 SymGetLineFromInlineContextW(
206 current_process,
207 addr,
208 inline_context,
209 0,
210 &mut 0,
211 line,
212 )
213 },
214 cb,
215 );
216 }
217 }
218 Some(())
219}
220
221unsafe fn do_resolve(
226 sym_from_addr: impl FnOnce(*mut SYMBOL_INFOW) -> BOOL,
227 get_line_from_addr: impl FnOnce(&mut IMAGEHLP_LINEW64) -> BOOL,
228 cb: &mut dyn FnMut(&super::Symbol),
229) {
230 const SIZE: usize = 2 * MAX_SYM_NAME as usize + mem::size_of::<SYMBOL_INFOW>();
231 let mut data = MaybeUninit::<Aligned8<[u8; SIZE]>>::zeroed();
232 let info = data.as_mut_ptr().cast::<SYMBOL_INFOW>();
233 unsafe { (*info).MaxNameLen = MAX_SYM_NAME as u32 };
234 unsafe { (*info).SizeOfStruct = 88 };
238
239 if sym_from_addr(info) != TRUE {
240 return;
241 }
242
243 let name_len = unsafe { cmp::min((*info).NameLen as usize, (*info).MaxNameLen as usize - 1) };
248 let name_ptr = (&raw const (*info).Name).cast::<u16>();
250
251 let mut name_buffer = [0_u8; 256];
254 let mut name_len = unsafe {
255 WideCharToMultiByte(
256 CP_UTF8,
257 0,
258 name_ptr,
259 name_len as i32,
260 name_buffer.as_mut_ptr(),
261 name_buffer.len() as i32,
262 core::ptr::null_mut(),
263 core::ptr::null_mut(),
264 ) as usize
265 };
266 if name_len == 0 {
267 name_len = name_buffer.len();
270 } else if name_len > name_buffer.len() {
271 return;
273 }
274 let name = ptr::addr_of!(name_buffer[..name_len]);
275
276 let mut line = IMAGEHLP_LINEW64 {
277 SizeOfStruct: 0,
278 Key: core::ptr::null_mut(),
279 LineNumber: 0,
280 FileName: core::ptr::null_mut(),
281 Address: 0,
282 };
283 line.SizeOfStruct = mem::size_of::<IMAGEHLP_LINEW64>() as u32;
284
285 let mut filename = None;
286 let mut lineno = None;
287 if get_line_from_addr(&mut line) == TRUE {
288 lineno = Some(line.LineNumber);
289
290 let base = line.FileName;
291 let mut len = 0;
292 while unsafe { *base.offset(len) != 0 } {
293 len += 1;
294 }
295
296 let len = len as usize;
297
298 unsafe {
299 filename = Some(ptr_from_ref(slice::from_raw_parts(base, len)));
300 }
301 }
302
303 cb(&super::Symbol {
304 inner: Symbol {
305 name,
306 addr: unsafe { (*info).Address } as *mut _,
308 line: lineno,
309 filename,
310 _filename_cache: unsafe { cache(filename) },
311 _marker: marker::PhantomData,
312 },
313 })
314}
315
316#[cfg(feature = "std")]
317unsafe fn cache(filename: Option<*const [u16]>) -> Option<::std::ffi::OsString> {
318 use std::os::windows::ffi::OsStringExt;
319 unsafe { filename.map(|f| ::std::ffi::OsString::from_wide(&*f)) }
320}
321
322#[cfg(not(feature = "std"))]
323unsafe fn cache(_filename: Option<*const [u16]>) {}
324
325pub unsafe fn clear_symbol_cache() {}