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
initialize()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
120 ///////////////////// External functions //////////////////////////
121
122 // All outside facing functions are synchronized. Also, we run
123 // initialization on first touch.
124
125 static CRITICAL_SECTION g_cs;
126
127 namespace { // Do not export.
128 class WindowsDbgHelpEntry {
129 public:
WindowsDbgHelpEntry()130 WindowsDbgHelpEntry() {
131 ::EnterCriticalSection(&g_cs);
132 if (g_state == state_uninitialized) {
133 initialize();
134 }
135 }
~WindowsDbgHelpEntry()136 ~WindowsDbgHelpEntry() {
137 ::LeaveCriticalSection(&g_cs);
138 }
139 };
140 }
141
142 // Called at DLL_PROCESS_ATTACH.
pre_initialize()143 void WindowsDbgHelp::pre_initialize() {
144 ::InitializeCriticalSection(&g_cs);
145 }
146
symSetOptions(DWORD arg)147 DWORD WindowsDbgHelp::symSetOptions(DWORD arg) {
148 WindowsDbgHelpEntry entry_guard;
149 if (g_pfn_SymSetOptions != NULL) {
150 return g_pfn_SymSetOptions(arg);
151 }
152 return 0;
153 }
154
symGetOptions(void)155 DWORD WindowsDbgHelp::symGetOptions(void) {
156 WindowsDbgHelpEntry entry_guard;
157 if (g_pfn_SymGetOptions != NULL) {
158 return g_pfn_SymGetOptions();
159 }
160 return 0;
161 }
162
symInitialize(HANDLE hProcess,PCTSTR UserSearchPath,BOOL fInvadeProcess)163 BOOL WindowsDbgHelp::symInitialize(HANDLE hProcess, PCTSTR UserSearchPath, BOOL fInvadeProcess) {
164 WindowsDbgHelpEntry entry_guard;
165 if (g_pfn_SymInitialize != NULL) {
166 return g_pfn_SymInitialize(hProcess, UserSearchPath, fInvadeProcess);
167 }
168 return FALSE;
169 }
170
symGetSymFromAddr64(HANDLE hProcess,DWORD64 the_address,PDWORD64 Displacement,PIMAGEHLP_SYMBOL64 Symbol)171 BOOL WindowsDbgHelp::symGetSymFromAddr64(HANDLE hProcess, DWORD64 the_address,
172 PDWORD64 Displacement, PIMAGEHLP_SYMBOL64 Symbol) {
173 WindowsDbgHelpEntry entry_guard;
174 if (g_pfn_SymGetSymFromAddr64 != NULL) {
175 return g_pfn_SymGetSymFromAddr64(hProcess, the_address, Displacement, Symbol);
176 }
177 return FALSE;
178 }
179
unDecorateSymbolName(const char * DecoratedName,char * UnDecoratedName,DWORD UndecoratedLength,DWORD Flags)180 DWORD WindowsDbgHelp::unDecorateSymbolName(const char* DecoratedName, char* UnDecoratedName,
181 DWORD UndecoratedLength, DWORD Flags) {
182 WindowsDbgHelpEntry entry_guard;
183 if (g_pfn_UnDecorateSymbolName != NULL) {
184 return g_pfn_UnDecorateSymbolName(DecoratedName, UnDecoratedName, UndecoratedLength, Flags);
185 }
186 if (UnDecoratedName != NULL && UndecoratedLength > 0) {
187 UnDecoratedName[0] = '\0';
188 }
189 return 0;
190 }
191
symSetSearchPath(HANDLE hProcess,PCTSTR SearchPath)192 BOOL WindowsDbgHelp::symSetSearchPath(HANDLE hProcess, PCTSTR SearchPath) {
193 WindowsDbgHelpEntry entry_guard;
194 if (g_pfn_SymSetSearchPath != NULL) {
195 return g_pfn_SymSetSearchPath(hProcess, SearchPath);
196 }
197 return FALSE;
198 }
199
symGetSearchPath(HANDLE hProcess,PTSTR SearchPath,int SearchPathLength)200 BOOL WindowsDbgHelp::symGetSearchPath(HANDLE hProcess, PTSTR SearchPath, int SearchPathLength) {
201 WindowsDbgHelpEntry entry_guard;
202 if (g_pfn_SymGetSearchPath != NULL) {
203 return g_pfn_SymGetSearchPath(hProcess, SearchPath, SearchPathLength);
204 }
205 return FALSE;
206 }
207
stackWalk64(DWORD MachineType,HANDLE hProcess,HANDLE hThread,LPSTACKFRAME64 StackFrame,PVOID ContextRecord)208 BOOL WindowsDbgHelp::stackWalk64(DWORD MachineType,
209 HANDLE hProcess,
210 HANDLE hThread,
211 LPSTACKFRAME64 StackFrame,
212 PVOID ContextRecord) {
213 WindowsDbgHelpEntry entry_guard;
214 if (g_pfn_StackWalk64 != NULL) {
215 return g_pfn_StackWalk64(MachineType, hProcess, hThread, StackFrame,
216 ContextRecord,
217 NULL, // ReadMemoryRoutine
218 g_pfn_SymFunctionTableAccess64, // FunctionTableAccessRoutine,
219 g_pfn_SymGetModuleBase64, // GetModuleBaseRoutine
220 NULL // TranslateAddressRoutine
221 );
222 }
223 return FALSE;
224 }
225
symFunctionTableAccess64(HANDLE hProcess,DWORD64 AddrBase)226 PVOID WindowsDbgHelp::symFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase) {
227 WindowsDbgHelpEntry entry_guard;
228 if (g_pfn_SymFunctionTableAccess64 != NULL) {
229 return g_pfn_SymFunctionTableAccess64(hProcess, AddrBase);
230 }
231 return NULL;
232 }
233
symGetModuleBase64(HANDLE hProcess,DWORD64 dwAddr)234 DWORD64 WindowsDbgHelp::symGetModuleBase64(HANDLE hProcess, DWORD64 dwAddr) {
235 WindowsDbgHelpEntry entry_guard;
236 if (g_pfn_SymGetModuleBase64 != NULL) {
237 return g_pfn_SymGetModuleBase64(hProcess, dwAddr);
238 }
239 return 0;
240 }
241
miniDumpWriteDump(HANDLE hProcess,DWORD ProcessId,HANDLE hFile,MINIDUMP_TYPE DumpType,PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,PMINIDUMP_CALLBACK_INFORMATION CallbackParam)242 BOOL WindowsDbgHelp::miniDumpWriteDump(HANDLE hProcess, DWORD ProcessId, HANDLE hFile,
243 MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
244 PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
245 PMINIDUMP_CALLBACK_INFORMATION CallbackParam) {
246 WindowsDbgHelpEntry entry_guard;
247 if (g_pfn_MiniDumpWriteDump != NULL) {
248 return g_pfn_MiniDumpWriteDump(hProcess, ProcessId, hFile, DumpType,
249 ExceptionParam, UserStreamParam, CallbackParam);
250 }
251 return FALSE;
252 }
253
symGetLineFromAddr64(HANDLE hProcess,DWORD64 dwAddr,PDWORD pdwDisplacement,PIMAGEHLP_LINE64 Line)254 BOOL WindowsDbgHelp::symGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr,
255 PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line) {
256 WindowsDbgHelpEntry entry_guard;
257 if (g_pfn_SymGetLineFromAddr64 != NULL) {
258 return g_pfn_SymGetLineFromAddr64(hProcess, dwAddr, pdwDisplacement, Line);
259 }
260 return FALSE;
261 }
262
263 // Print one liner describing state (if library loaded, which functions are
264 // missing - if any, and the dbhelp API version)
print_state_on(outputStream * st)265 void WindowsDbgHelp::print_state_on(outputStream* st) {
266 // Note: We should not lock while printing, but this should be
267 // safe to do without lock anyway.
268 st->print("dbghelp: ");
269
270 if (g_state == state_uninitialized) {
271 st->print("uninitialized.");
272 } else if (g_state == state_error) {
273 st->print("loading error: %u", g_dll_load_error);
274 } else {
275 st->print("loaded successfully ");
276
277 // We may want to print dll file name here - which may be interesting for
278 // cases where more than one version exists on the system, e.g. with a
279 // debugging sdk separately installed. But we get the file name in the DLL
280 // section of the hs-err file too, so this may be redundant.
281
282 // Print version.
283 st->print("- version: %u.%u.%u",
284 g_version.MajorVersion, g_version.MinorVersion, g_version.Revision);
285
286 // Print any functions which failed to load.
287 int num_missing = 0;
288 st->print(" - missing functions: ");
289
290 #define CHECK_AND_PRINT_IF_NULL(functionname) \
291 if (g_pfn_##functionname == NULL) { \
292 st->print("%s" #functionname, ((num_missing > 0) ? ", " : "")); \
293 num_missing ++; \
294 }
295
296 FOR_ALL_FUNCTIONS(CHECK_AND_PRINT_IF_NULL)
297
298 if (num_missing == 0) {
299 st->print("none");
300 }
301 }
302 st->cr();
303 }
304
305