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