| /* | |
| * Copyright (c) 2017, 2025, Oracle and/or its affiliates. All rights reserved. | |
| * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. | |
| * | |
| * This code is free software; you can redistribute it and/or modify it | |
| * under the terms of the GNU General Public License version 2 only, as | |
| * published by the Free Software Foundation. | |
| * | |
| * This code is distributed in the hope that it will be useful, but WITHOUT | |
| * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| * version 2 for more details (a copy is included in the LICENSE file that | |
| * accompanied this code). | |
| * | |
| * You should have received a copy of the GNU General Public License version | |
| * 2 along with this work; if not, write to the Free Software Foundation, | |
| * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. | |
| * | |
| * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA | |
| * or visit www.oracle.com if you need additional information or have any | |
| * questions. | |
| * | |
| */ | |
| #include "utilities/ostream.hpp" | |
| #include "windbghelp.hpp" | |
| #include <windows.h> | |
| typedef DWORD (WINAPI *pfn_SymSetOptions)(DWORD); | |
| typedef DWORD (WINAPI *pfn_SymGetOptions)(void); | |
| typedef BOOL (WINAPI *pfn_SymInitialize)(HANDLE, PCTSTR, BOOL); | |
| typedef BOOL (WINAPI *pfn_SymGetSymFromAddr64)(HANDLE, DWORD64, PDWORD64, PIMAGEHLP_SYMBOL64); | |
| typedef DWORD (WINAPI *pfn_UnDecorateSymbolName)(const char*, char*, DWORD, DWORD); | |
| typedef BOOL (WINAPI *pfn_SymSetSearchPath)(HANDLE, PCTSTR); | |
| typedef BOOL (WINAPI *pfn_SymGetSearchPath)(HANDLE, PTSTR, int); | |
| typedef BOOL (WINAPI *pfn_SymRefreshModuleList)(HANDLE); | |
| typedef BOOL (WINAPI *pfn_StackWalk64)(DWORD MachineType, | |
| HANDLE hProcess, | |
| HANDLE hThread, | |
| LPSTACKFRAME64 StackFrame, | |
| PVOID ContextRecord, | |
| PREAD_PROCESS_MEMORY_ROUTINE64 ReadMemoryRoutine, | |
| PFUNCTION_TABLE_ACCESS_ROUTINE64 FunctionTableAccessRoutine, | |
| PGET_MODULE_BASE_ROUTINE64 GetModuleBaseRoutine, | |
| PTRANSLATE_ADDRESS_ROUTINE64 TranslateAddress); | |
| typedef PVOID (WINAPI *pfn_SymFunctionTableAccess64)(HANDLE hProcess, DWORD64 AddrBase); | |
| typedef DWORD64 (WINAPI *pfn_SymGetModuleBase64)(HANDLE hProcess, DWORD64 dwAddr); | |
| typedef BOOL (WINAPI *pfn_MiniDumpWriteDump) (HANDLE hProcess, DWORD ProcessId, HANDLE hFile, | |
| MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, | |
| PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, | |
| PMINIDUMP_CALLBACK_INFORMATION CallbackParam); | |
| typedef BOOL (WINAPI *pfn_SymGetLineFromAddr64) (HANDLE hProcess, DWORD64 dwAddr, | |
| PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line); | |
| typedef LPAPI_VERSION (WINAPI *pfn_ImagehlpApiVersion)(void); | |
| // Add functions as needed. | |
| #define FOR_ALL_FUNCTIONS(DO) \ | |
| DO(ImagehlpApiVersion) \ | |
| DO(SymGetOptions) \ | |
| DO(SymSetOptions) \ | |
| DO(SymInitialize) \ | |
| DO(SymGetSymFromAddr64) \ | |
| DO(UnDecorateSymbolName) \ | |
| DO(SymSetSearchPath) \ | |
| DO(SymGetSearchPath) \ | |
| DO(StackWalk64) \ | |
| DO(SymFunctionTableAccess64) \ | |
| DO(SymGetModuleBase64) \ | |
| DO(MiniDumpWriteDump) \ | |
| DO(SymGetLineFromAddr64) \ | |
| DO(SymRefreshModuleList) | |
| #define DECLARE_FUNCTION_POINTER(functionname) \ | |
| static pfn_##functionname g_pfn_##functionname; | |
| FOR_ALL_FUNCTIONS(DECLARE_FUNCTION_POINTER) | |
| static HMODULE g_dll_handle = nullptr; | |
| static DWORD g_dll_load_error = 0; | |
| static API_VERSION g_version = { 0, 0, 0, 0 }; | |
| static enum { | |
| state_uninitialized = 0, | |
| state_ready = 1, | |
| state_error = 2 | |
| } g_state = state_uninitialized; | |
| static void initialize() { | |
| assert(g_state == state_uninitialized, "wrong sequence"); | |
| g_state = state_error; | |
| g_dll_handle = ::LoadLibrary("DBGHELP.DLL"); | |
| if (g_dll_handle == nullptr) { | |
| g_dll_load_error = ::GetLastError(); | |
| } else { | |
| // Note: We loaded the DLL successfully. From here on we count | |
| // initialization as success. We still may fail to load all of the | |
| // desired function pointers successfully, but DLL may still be usable | |
| // enough for our purposes. | |
| g_state = state_ready; | |
| #define DO_RESOLVE(functionname) \ | |
| g_pfn_##functionname = (pfn_##functionname) ::GetProcAddress(g_dll_handle, #functionname); | |
| FOR_ALL_FUNCTIONS(DO_RESOLVE) | |
| // Retrieve version information. | |
| if (g_pfn_ImagehlpApiVersion) { | |
| const API_VERSION* p = g_pfn_ImagehlpApiVersion(); | |
| memcpy(&g_version, p, sizeof(API_VERSION)); | |
| } | |
| } | |
| } | |
| ///////////////////// External functions ////////////////////////// | |
| // All outside facing functions are synchronized. Also, we run | |
| // initialization on first touch. | |
| static CRITICAL_SECTION g_cs; | |
| namespace { // Do not export. | |
| class WindowsDbgHelpEntry { | |
| public: | |
| WindowsDbgHelpEntry() { | |
| ::EnterCriticalSection(&g_cs); | |
| if (g_state == state_uninitialized) { | |
| initialize(); | |
| } | |
| } | |
| ~WindowsDbgHelpEntry() { | |
| ::LeaveCriticalSection(&g_cs); | |
| } | |
| }; | |
| } | |
| // Called at DLL_PROCESS_ATTACH for dynamic builds, and from os::init() for static builds. | |
| void WindowsDbgHelp::pre_initialize() { | |
| ::InitializeCriticalSection(&g_cs); | |
| } | |
| DWORD WindowsDbgHelp::symSetOptions(DWORD arg) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymSetOptions != nullptr) { | |
| return g_pfn_SymSetOptions(arg); | |
| } | |
| return 0; | |
| } | |
| DWORD WindowsDbgHelp::symGetOptions(void) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymGetOptions != nullptr) { | |
| return g_pfn_SymGetOptions(); | |
| } | |
| return 0; | |
| } | |
| BOOL WindowsDbgHelp::symInitialize(HANDLE hProcess, PCTSTR UserSearchPath, BOOL fInvadeProcess) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymInitialize != nullptr) { | |
| return g_pfn_SymInitialize(hProcess, UserSearchPath, fInvadeProcess); | |
| } | |
| return FALSE; | |
| } | |
| BOOL WindowsDbgHelp::symGetSymFromAddr64(HANDLE hProcess, DWORD64 the_address, | |
| PDWORD64 Displacement, PIMAGEHLP_SYMBOL64 Symbol) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymGetSymFromAddr64 != nullptr) { | |
| return g_pfn_SymGetSymFromAddr64(hProcess, the_address, Displacement, Symbol); | |
| } | |
| return FALSE; | |
| } | |
| DWORD WindowsDbgHelp::unDecorateSymbolName(const char* DecoratedName, char* UnDecoratedName, | |
| DWORD UndecoratedLength, DWORD Flags) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_UnDecorateSymbolName != nullptr) { | |
| return g_pfn_UnDecorateSymbolName(DecoratedName, UnDecoratedName, UndecoratedLength, Flags); | |
| } | |
| if (UnDecoratedName != nullptr && UndecoratedLength > 0) { | |
| UnDecoratedName[0] = '\0'; | |
| } | |
| return 0; | |
| } | |
| BOOL WindowsDbgHelp::symSetSearchPath(HANDLE hProcess, PCTSTR SearchPath) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymSetSearchPath != nullptr) { | |
| return g_pfn_SymSetSearchPath(hProcess, SearchPath); | |
| } | |
| return FALSE; | |
| } | |
| BOOL WindowsDbgHelp::symGetSearchPath(HANDLE hProcess, PTSTR SearchPath, int SearchPathLength) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymGetSearchPath != nullptr) { | |
| return g_pfn_SymGetSearchPath(hProcess, SearchPath, SearchPathLength); | |
| } | |
| return FALSE; | |
| } | |
| BOOL WindowsDbgHelp::symRefreshModuleList(HANDLE hProcess) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymRefreshModuleList != nullptr) { | |
| return g_pfn_SymRefreshModuleList(hProcess); | |
| } | |
| return FALSE; | |
| } | |
| BOOL WindowsDbgHelp::stackWalk64(DWORD MachineType, | |
| HANDLE hProcess, | |
| HANDLE hThread, | |
| LPSTACKFRAME64 StackFrame, | |
| PVOID ContextRecord) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_StackWalk64 != nullptr) { | |
| return g_pfn_StackWalk64(MachineType, hProcess, hThread, StackFrame, | |
| ContextRecord, | |
| nullptr, // ReadMemoryRoutine | |
| g_pfn_SymFunctionTableAccess64, // FunctionTableAccessRoutine, | |
| g_pfn_SymGetModuleBase64, // GetModuleBaseRoutine | |
| nullptr // TranslateAddressRoutine | |
| ); | |
| } | |
| return FALSE; | |
| } | |
| PVOID WindowsDbgHelp::symFunctionTableAccess64(HANDLE hProcess, DWORD64 AddrBase) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymFunctionTableAccess64 != nullptr) { | |
| return g_pfn_SymFunctionTableAccess64(hProcess, AddrBase); | |
| } | |
| return nullptr; | |
| } | |
| DWORD64 WindowsDbgHelp::symGetModuleBase64(HANDLE hProcess, DWORD64 dwAddr) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymGetModuleBase64 != nullptr) { | |
| return g_pfn_SymGetModuleBase64(hProcess, dwAddr); | |
| } | |
| return 0; | |
| } | |
| BOOL WindowsDbgHelp::miniDumpWriteDump(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, | |
| MINIDUMP_TYPE DumpType, PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, | |
| PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, | |
| PMINIDUMP_CALLBACK_INFORMATION CallbackParam) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_MiniDumpWriteDump != nullptr) { | |
| return g_pfn_MiniDumpWriteDump(hProcess, ProcessId, hFile, DumpType, | |
| ExceptionParam, UserStreamParam, CallbackParam); | |
| } | |
| return FALSE; | |
| } | |
| BOOL WindowsDbgHelp::symGetLineFromAddr64(HANDLE hProcess, DWORD64 dwAddr, | |
| PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line) { | |
| WindowsDbgHelpEntry entry_guard; | |
| if (g_pfn_SymGetLineFromAddr64 != nullptr) { | |
| return g_pfn_SymGetLineFromAddr64(hProcess, dwAddr, pdwDisplacement, Line); | |
| } | |
| return FALSE; | |
| } | |
| // Print one liner describing state (if library loaded, which functions are | |
| // missing - if any, and the dbhelp API version) | |
| void WindowsDbgHelp::print_state_on(outputStream* st) { | |
| // Note: We should not lock while printing, but this should be | |
| // safe to do without lock anyway. | |
| st->print("dbghelp: "); | |
| if (g_state == state_uninitialized) { | |
| st->print("uninitialized."); | |
| } else if (g_state == state_error) { | |
| st->print("loading error: %u", g_dll_load_error); | |
| } else { | |
| st->print("loaded successfully "); | |
| // We may want to print dll file name here - which may be interesting for | |
| // cases where more than one version exists on the system, e.g. with a | |
| // debugging sdk separately installed. But we get the file name in the DLL | |
| // section of the hs-err file too, so this may be redundant. | |
| // Print version. | |
| st->print("- version: %u.%u.%u", | |
| g_version.MajorVersion, g_version.MinorVersion, g_version.Revision); | |
| // Print any functions which failed to load. | |
| int num_missing = 0; | |
| st->print(" - missing functions: "); | |
| #define CHECK_AND_PRINT_IF_NULL(functionname) \ | |
| if (g_pfn_##functionname == nullptr) { \ | |
| st->print("%s" #functionname, ((num_missing > 0) ? ", " : "")); \ | |
| num_missing ++; \ | |
| } | |
| FOR_ALL_FUNCTIONS(CHECK_AND_PRINT_IF_NULL) | |
| if (num_missing == 0) { | |
| st->print("none"); | |
| } | |
| } | |
| st->cr(); | |
| } |