|
1 /* |
|
2 * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. |
|
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
|
4 * |
|
5 * This code is free software; you can redistribute it and/or modify it |
|
6 * under the terms of the GNU General Public License version 2 only, as |
|
7 * published by the Free Software Foundation. |
|
8 * |
|
9 * This code is distributed in the hope that it will be useful, but WITHOUT |
|
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
|
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
|
12 * version 2 for more details (a copy is included in the LICENSE file that |
|
13 * accompanied this code). |
|
14 * |
|
15 * You should have received a copy of the GNU General Public License version |
|
16 * 2 along with this work; if not, write to the Free Software Foundation, |
|
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
|
18 * |
|
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
|
20 * or visit www.oracle.com if you need additional information or have any |
|
21 * questions. |
|
22 * |
|
23 */ |
|
24 |
|
25 #include "precompiled.hpp" |
|
26 #include "utilities/ostream.hpp" |
|
27 #include "windbghelp.hpp" |
|
28 |
|
29 #include <windows.h> |
|
30 |
|
31 typedef DWORD (WINAPI *pfn_SymSetOptions)(DWORD); |
|
32 typedef DWORD (WINAPI *pfn_SymGetOptions)(void); |
|
33 typedef BOOL (WINAPI *pfn_SymInitialize)(HANDLE, PCTSTR, BOOL); |
|
34 typedef BOOL (WINAPI *pfn_SymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); |
|
35 typedef DWORD (WINAPI *pfn_UnDecorateSymbolName)(const char*, char*, DWORD, DWORD); |
|
36 typedef BOOL (WINAPI *pfn_SymSetSearchPath)(HANDLE, PCTSTR); |
|
37 typedef BOOL (WINAPI *pfn_SymGetSearchPath)(HANDLE, PTSTR, int); |
|
38 typedef BOOL (WINAPI *pfn_StackWalk64)(DWORD MachineType, |
|
39 HANDLE hProcess, |
|
40 HANDLE hThread, |
|
41 LPSTACKFRAME64 StackFrame, |
|
42 PVOID ContextRecord, |
|
43 PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, |
|
44 PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, |
|
45 PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, |
|
46 PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); |
|
47 typedef PVOID (WINAPI *pfn_SymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase); |
|
48 typedef DWORD64 (WINAPI *pfn_SymGetModuleBase64)(HANDLE hProcess, DWORD64 dwAddr); |
|
49 typedef BOOL (WINAPI *pfn_MiniDumpWriteDump) (HANDLE hProcess, DWORD ProcessId, HANDLE hFile, |
|
50 MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, |
|
51 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, |
|
52 PMINIDUMP_CALLBACK_INFORMATION CallbackParam); |
|
53 typedef BOOL (WINAPI *pfn_SymGetLineFromAddr64) (HANDLE hProcess, DWORD64 dwAddr, |
|
54 PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line); |
|
55 typedef LPAPI_VERSION (WINAPI *pfn_ImagehlpApiVersion)(void); |
|
56 |
|
57 // Add functions as needed. |
|
58 #define FOR_ALL_FUNCTIONS(DO) \ |
|
59 DO(ImagehlpApiVersion) \ |
|
60 DO(SymGetOptions) \ |
|
61 DO(SymSetOptions) \ |
|
62 DO(SymInitialize) \ |
|
63 DO(SymGetSymFromAddr64) \ |
|
64 DO(UnDecorateSymbolName) \ |
|
65 DO(SymSetSearchPath) \ |
|
66 DO(SymGetSearchPath) \ |
|
67 DO(StackWalk64) \ |
|
68 DO(SymFunctionTableAccess64) \ |
|
69 DO(SymGetModuleBase64) \ |
|
70 DO(MiniDumpWriteDump) \ |
|
71 DO(SymGetLineFromAddr64) |
|
72 |
|
73 |
|
74 #define DECLARE_FUNCTION_POINTER(functionname) \ |
|
75 static pfn_##functionname g_pfn_##functionname; |
|
76 |
|
77 FOR_ALL_FUNCTIONS(DECLARE_FUNCTION_POINTER) |
|
78 |
|
79 |
|
80 static HMODULE g_dll_handle = NULL; |
|
81 static DWORD g_dll_load_error = 0; |
|
82 static API_VERSION g_version = { 0, 0, 0, 0 }; |
|
83 |
|
84 static enum { |
|
85 state_uninitialized = 0, |
|
86 state_ready = 1, |
|
87 state_error = 2 |
|
88 } g_state = state_uninitialized; |
|
89 |
|
90 static void initialize() { |
|
91 |
|
92 assert(g_state == state_uninitialized, "wrong sequence"); |
|
93 g_state = state_error; |
|
94 |
|
95 g_dll_handle = ::LoadLibrary("DBGHELP.DLL"); |
|
96 if (g_dll_handle == NULL) { |
|
97 g_dll_load_error = ::GetLastError(); |
|
98 } else { |
|
99 // Note: We loaded the DLL successfully. From here on we count |
|
100 // initialization as success. We still may fail to load all of the |
|
101 // desired function pointers successfully, but DLL may still be usable |
|
102 // enough for our purposes. |
|
103 g_state = state_ready; |
|
104 |
|
105 #define DO_RESOLVE(functionname) \ |
|
106 g_pfn_##functionname = (pfn_##functionname) ::GetProcAddress(g_dll_handle, #functionname); |
|
107 |
|
108 FOR_ALL_FUNCTIONS(DO_RESOLVE) |
|
109 |
|
110 // Retrieve version information. |
|
111 if (g_pfn_ImagehlpApiVersion) { |
|
112 const API_VERSION* p = g_pfn_ImagehlpApiVersion(); |
|
113 memcpy(&g_version, p, sizeof(API_VERSION)); |
|
114 } |
|
115 } |
|
116 |
|
117 } |
|
118 |
|
119 ///////////////////// External functions ////////////////////////// |
|
120 |
|
121 // All outside facing functions are synchronized. Also, we run |
|
122 // initialization on first touch. |
|
123 |
|
124 |
|
125 // Call InitializeCriticalSection as early as possible. |
|
126 class CritSect { |
|
127 CRITICAL_SECTION cs; |
|
128 public: |
|
129 CritSect() { ::InitializeCriticalSection(&cs); } |
|
130 void enter() { ::EnterCriticalSection(&cs); } |
|
131 void leave() { ::LeaveCriticalSection(&cs); } |
|
132 }; |
|
133 |
|
134 static CritSect g_cs; |
|
135 |
|
136 class EntryGuard { |
|
137 public: |
|
138 EntryGuard() { |
|
139 g_cs.enter(); |
|
140 if (g_state == state_uninitialized) { |
|
141 initialize(); |
|
142 } |
|
143 } |
|
144 ~EntryGuard() { |
|
145 g_cs.leave(); |
|
146 } |
|
147 }; |
|
148 |
|
149 DWORD WindowsDbgHelp::symSetOptions(DWORD arg) { |
|
150 EntryGuard entry_guard; |
|
151 if (g_pfn_SymSetOptions != NULL) { |
|
152 return g_pfn_SymSetOptions(arg); |
|
153 } |
|
154 return 0; |
|
155 } |
|
156 |
|
157 DWORD WindowsDbgHelp::symGetOptions(void) { |
|
158 EntryGuard entry_guard; |
|
159 if (g_pfn_SymGetOptions != NULL) { |
|
160 return g_pfn_SymGetOptions(); |
|
161 } |
|
162 return 0; |
|
163 } |
|
164 |
|
165 BOOL WindowsDbgHelp::symInitialize(HANDLE hProcess, PCTSTR UserSearchPath, BOOL fInvadeProcess) { |
|
166 EntryGuard entry_guard; |
|
167 if (g_pfn_SymInitialize != NULL) { |
|
168 return g_pfn_SymInitialize(hProcess, UserSearchPath, fInvadeProcess); |
|
169 } |
|
170 return FALSE; |
|
171 } |
|
172 |
|
173 BOOL WindowsDbgHelp::symGetSymFromAddr64(HANDLE hProcess, DWORD64 the_address, |
|
174 PDWORD64 Displacement, PIMAGEHLP_SYMBOL64 Symbol) { |
|
175 EntryGuard entry_guard; |
|
176 if (g_pfn_SymGetSymFromAddr64 != NULL) { |
|
177 return g_pfn_SymGetSymFromAddr64(hProcess, the_address, Displacement, Symbol); |
|
178 } |
|
179 return FALSE; |
|
180 } |
|
181 |
|
182 DWORD WindowsDbgHelp::unDecorateSymbolName(const char* DecoratedName, char* UnDecoratedName, |
|
183 DWORD UndecoratedLength, DWORD Flags) { |
|
184 EntryGuard entry_guard; |
|
185 if (g_pfn_UnDecorateSymbolName != NULL) { |
|
186 return g_pfn_UnDecorateSymbolName(DecoratedName, UnDecoratedName, UndecoratedLength, Flags); |
|
187 } |
|
188 if (UnDecoratedName != NULL && UndecoratedLength > 0) { |
|
189 UnDecoratedName[0] = '\0'; |
|
190 } |
|
191 return 0; |
|
192 } |
|
193 |
|
194 BOOL WindowsDbgHelp::symSetSearchPath(HANDLE hProcess, PCTSTR SearchPath) { |
|
195 EntryGuard entry_guard; |
|
196 if (g_pfn_SymSetSearchPath != NULL) { |
|
197 return g_pfn_SymSetSearchPath(hProcess, SearchPath); |
|
198 } |
|
199 return FALSE; |
|
200 } |
|
201 |
|
202 BOOL WindowsDbgHelp::symGetSearchPath(HANDLE hProcess, PTSTR SearchPath, int SearchPathLength) { |
|
203 EntryGuard entry_guard; |
|
204 if (g_pfn_SymGetSearchPath != NULL) { |
|
205 return g_pfn_SymGetSearchPath(hProcess, SearchPath, SearchPathLength); |
|
206 } |
|
207 return FALSE; |
|
208 } |
|
209 |
|
210 BOOL WindowsDbgHelp::stackWalk64(DWORD MachineType, |
|
211 HANDLE hProcess, |
|
212 HANDLE hThread, |
|
213 LPSTACKFRAME64 StackFrame, |
|
214 PVOID ContextRecord) { |
|
215 EntryGuard entry_guard; |
|
216 if (g_pfn_StackWalk64 != NULL) { |
|
217 return g_pfn_StackWalk64(MachineType, hProcess, hThread, StackFrame, |
|
218 ContextRecord, |
|
219 NULL, // ReadMemoryRoutine |
|
220 g_pfn_SymFunctionTableAccess64, // FunctionTableAccessRoutine, |
|
221 g_pfn_SymGetModuleBase64, // GetModuleBaseRoutine |
|
222 NULL // TranslateAddressRoutine |
|
223 ); |
|
224 } |
|
225 return FALSE; |
|
226 } |
|
227 |
|
228 PVOID WindowsDbgHelp::symFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase) { |
|
229 EntryGuard entry_guard; |
|
230 if (g_pfn_SymFunctionTableAccess64 != NULL) { |
|
231 return g_pfn_SymFunctionTableAccess64(hProcess, AddrBase); |
|
232 } |
|
233 return NULL; |
|
234 } |
|
235 |
|
236 DWORD64 WindowsDbgHelp::symGetModuleBase64(HANDLE hProcess, DWORD64 dwAddr) { |
|
237 EntryGuard entry_guard; |
|
238 if (g_pfn_SymGetModuleBase64 != NULL) { |
|
239 return g_pfn_SymGetModuleBase64(hProcess, dwAddr); |
|
240 } |
|
241 return 0; |
|
242 } |
|
243 |
|
244 BOOL WindowsDbgHelp::miniDumpWriteDump(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, |
|
245 MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, |
|
246 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, |
|
247 PMINIDUMP_CALLBACK_INFORMATION CallbackParam) { |
|
248 EntryGuard entry_guard; |
|
249 if (g_pfn_MiniDumpWriteDump != NULL) { |
|
250 return g_pfn_MiniDumpWriteDump(hProcess, ProcessId, hFile, DumpType, |
|
251 ExceptionParam, UserStreamParam, CallbackParam); |
|
252 } |
|
253 return FALSE; |
|
254 } |
|
255 |
|
256 BOOL WindowsDbgHelp::symGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr, |
|
257 PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line) { |
|
258 EntryGuard entry_guard; |
|
259 if (g_pfn_SymGetLineFromAddr64 != NULL) { |
|
260 return g_pfn_SymGetLineFromAddr64(hProcess, dwAddr, pdwDisplacement, Line); |
|
261 } |
|
262 return FALSE; |
|
263 } |
|
264 |
|
265 // Print one liner describing state (if library loaded, which functions are |
|
266 // missing - if any, and the dbhelp API version) |
|
267 void WindowsDbgHelp::print_state_on(outputStream* st) { |
|
268 // Note: We should not lock while printing, but this should be |
|
269 // safe to do without lock anyway. |
|
270 st->print("dbghelp: "); |
|
271 |
|
272 if (g_state == state_uninitialized) { |
|
273 st->print("uninitialized."); |
|
274 } else if (g_state == state_error) { |
|
275 st->print("loading error: %u", g_dll_load_error); |
|
276 } else { |
|
277 st->print("loaded successfully "); |
|
278 |
|
279 // We may want to print dll file name here - which may be interesting for |
|
280 // cases where more than one version exists on the system, e.g. with a |
|
281 // debugging sdk separately installed. But we get the file name in the DLL |
|
282 // section of the hs-err file too, so this may be redundant. |
|
283 |
|
284 // Print version. |
|
285 st->print("- version: %u.%u.%u", |
|
286 g_version.MajorVersion, g_version.MinorVersion, g_version.Revision); |
|
287 |
|
288 // Print any functions which failed to load. |
|
289 int num_missing = 0; |
|
290 st->print(" - missing functions: "); |
|
291 |
|
292 #define CHECK_AND_PRINT_IF_NULL(functionname) \ |
|
293 if (g_pfn_##functionname == NULL) { \ |
|
294 st->print("%s" #functionname, ((num_missing > 0) ? ", " : "")); \ |
|
295 num_missing ++; \ |
|
296 } |
|
297 |
|
298 FOR_ALL_FUNCTIONS(CHECK_AND_PRINT_IF_NULL) |
|
299 |
|
300 if (num_missing == 0) { |
|
301 st->print("none"); |
|
302 } |
|
303 } |
|
304 st->cr(); |
|
305 } |
|
306 |