v / vlib / builtin / backtraces_windows.c.v
180 lines · 161 sloc · 5.5 KB · 1801798a416a1d83316ec43a5d0102cc21a6d3a9
Raw
1module builtin
2
3// dbghelp.h is already included in cheaders.v
4#flag windows -l dbghelp
5
6// SymbolInfo is used by print_backtrace_skipping_top_frames_msvc.
7pub struct SymbolInfo {
8pub mut:
9 f_size_of_struct u32 // must be 88 to be recognised by SymFromAddr
10 f_type_index u32 // Type Index of symbol
11 f_reserved [2]u64
12 f_index u32
13 f_size u32
14 f_mod_base u64 // Base Address of module containing this symbol
15 f_flags u32
16 f_value u64 // Value of symbol, ValuePresent should be 1
17 f_address u64 // Address of symbol including base address of module
18 f_register u32 // register holding value or pointer to value
19 f_scope u32 // scope of the symbol
20 f_tag u32 // pdb classification
21 f_name_len u32 // Actual length of name
22 f_max_name_len u32 // must be manually set
23 f_name u8 // must be calloc(f_max_name_len)
24}
25
26pub struct SymbolInfoContainer {
27pub mut:
28 syminfo SymbolInfo
29 f_name_rest [254]char
30}
31
32pub struct Line64 {
33pub mut:
34 f_size_of_struct u32
35 f_key voidptr
36 f_line_number u32
37 f_file_name &u8 = unsafe { nil }
38 f_address u64
39}
40
41// returns the current options mask
42fn C.SymSetOptions(symoptions u32) u32
43
44// returns handle
45fn C.GetCurrentProcess() voidptr
46
47fn C.SymInitialize(h_process voidptr, p_user_search_path &u8, b_invade_process i32) i32
48
49fn C.CaptureStackBackTrace(frames_to_skip u32, frames_to_capture u32, p_backtrace voidptr, p_backtrace_hash voidptr) u16
50
51$if windows {
52 $if msvc {
53 fn C.SymFromAddr(h_process voidptr, address u64, p_displacement voidptr, p_symbol voidptr) i32
54 fn C.SymGetLineFromAddr64(h_process voidptr, address u64, p_displacement voidptr, p_line &Line64) i32
55 }
56}
57
58// Ref - https://docs.microsoft.com/en-us/windows/win32/api/dbghelp/nf-dbghelp-symsetoptions
59const symopt_undname = 0x00000002
60const symopt_deferred_loads = 0x00000004
61const symopt_no_cpp = 0x00000008
62const symopt_load_lines = 0x00000010
63const symopt_include_32bit_modules = 0x00002000
64const symopt_allow_zero_address = 0x01000000
65const symopt_debug = u32(0x80000000)
66
67// print_backtrace_skipping_top_frames prints the backtrace skipping N top frames.
68pub fn print_backtrace_skipping_top_frames(skipframes int) bool {
69 $if msvc {
70 return print_backtrace_skipping_top_frames_msvc(skipframes)
71 }
72 $if tinyc {
73 return print_backtrace_skipping_top_frames_tcc(skipframes)
74 }
75 $if mingw {
76 return print_backtrace_skipping_top_frames_mingw(skipframes)
77 }
78 eprintln('print_backtrace_skipping_top_frames is not implemented')
79 return false
80}
81
82@[direct_array_access]
83fn print_backtrace_skipping_top_frames_msvc(skipframes int) bool {
84 $if msvc {
85 mut offset := u64(0)
86 backtraces := [100]voidptr{}
87 sic := SymbolInfoContainer{}
88 mut si := &sic.syminfo
89 si.f_size_of_struct = sizeof(SymbolInfo) // Note: C.SYMBOL_INFO is 88
90 si.f_max_name_len = sizeof(SymbolInfoContainer) - sizeof(SymbolInfo) - 1
91 fname := &char(&si.f_name)
92 mut sline64 := Line64{
93 f_file_name: &u8(unsafe { nil })
94 }
95 sline64.f_size_of_struct = sizeof(Line64)
96
97 handle := C.GetCurrentProcess()
98 defer {
99 C.SymCleanup(handle)
100 }
101
102 C.SymSetOptions(symopt_debug | symopt_load_lines | symopt_undname)
103
104 syminitok := C.SymInitialize(handle, 0, 1)
105 if syminitok != 1 {
106 eprintln('Failed getting process: Aborting backtrace.\n')
107 return false
108 }
109
110 frames := int(C.CaptureStackBackTrace(skipframes + 1, 100, &backtraces[0], 0))
111 if frames < 2 {
112 eprintln('C.CaptureStackBackTrace returned less than 2 frames')
113 return false
114 }
115 for i in 0 .. frames {
116 frame_addr := backtraces[i]
117 if C.SymFromAddr(handle, frame_addr, &offset, si) == 1 {
118 nframe := frames - i - 1
119 mut lineinfo := ''
120 if C.SymGetLineFromAddr64(handle, frame_addr, &offset, &sline64) == 1 {
121 file_name := unsafe { tos3(sline64.f_file_name) }
122 lnumber := sline64.f_line_number
123 lineinfo = file_name + ':' + i64(lnumber).str()
124 } else {
125 // addr:
126 lineinfo = '?? : address = 0x' + ptr_str(frame_addr)
127 }
128 sfunc := demangle_v_symbol(unsafe { tos3(fname) })
129 snframe := i64(nframe).str()
130 eprint_space_padding(snframe, 2)
131 eprint(': ')
132 eprint(sfunc)
133 eprint_space_padding(sfunc, 25)
134 eprint(' ')
135 eprintln(lineinfo)
136 } else {
137 // https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes
138 cerr := int(C.GetLastError())
139 eprint('SymFromAddr failure: ')
140 eprint(i64(cerr).str())
141 if cerr == 87 {
142 eprintln(' = The parameter is incorrect)')
143 } else if cerr == 487 {
144 // probably caused because the .pdb isn't in the executable folder
145 eprintln(' = Attempt to access invalid address (Verify that you have the .pdb file in the right folder.)')
146 } else {
147 eprintln(' (see https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes)')
148 }
149 }
150 }
151 return true
152 } $else {
153 eprintln('print_backtrace_skipping_top_frames_msvc must be called only when the compiler is msvc')
154 return false
155 }
156}
157
158fn print_backtrace_skipping_top_frames_mingw(skipframes int) bool {
159 eprintln('print_backtrace_skipping_top_frames_mingw is not implemented')
160 return false
161}
162
163fn C.tcc_backtrace(fmt &char) i32
164
165fn print_backtrace_skipping_top_frames_tcc(skipframes int) bool {
166 $if tinyc {
167 $if no_backtrace ? {
168 eprintln('backtraces are disabled')
169 return false
170 } $else {
171 C.tcc_backtrace(c'Backtrace')
172 return true
173 }
174 } $else {
175 eprintln('print_backtrace_skipping_top_frames_tcc must be called only when the compiler is tcc')
176 return false
177 }
178 // Not reachable, but it looks like it's not detectable by V
179 return false
180}
181