Path: blob/master/src/hotspot/os/windows/symbolengine.cpp
40931 views
/*1* Copyright (c) 2017, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*22*/2324#include "precompiled.hpp"25#include "utilities/globalDefinitions.hpp"26#include "symbolengine.hpp"27#include "utilities/debug.hpp"28#include "utilities/ostream.hpp"29#include "windbghelp.hpp"3031#include <windows.h>3233#include <imagehlp.h>34#include <psapi.h>35363738// This code may be invoked normally but also as part of error reporting39// In the latter case, we may run under tight memory constraints (native oom)40// or in a stack overflow situation or the C heap may be corrupted. We may41// run very early before VM initialization or very late when C exit handlers42// run. In all these cases, callstacks would still be nice, so lets be robust.43//44// We need a number of buffers - for the pdb search path, module handle45// lists, for demangled symbols, etc.46//47// These buffers, while typically small, may need to be large for corner48// cases (e.g. templatized C++ symbols, or many DLLs loaded). Where do we49// allocate them?50//51// We may be in error handling for a stack overflow, so lets not put them on52// the stack.53//54// Dynamically allocating them may fail if we are handling a native OOM. It55// is also a bit dangerous, as the C heap may be corrupted already.56//57// That leaves pre-allocating them globally, which is safe and should always58// work (if we synchronize access) but incurs an undesirable footprint for59// non-error cases.60//61// We follow a two-way strategy: Allocate the buffers on the C heap in a62// reasonable large size. Failing that, fall back to static preallocated63// buffers. The size of the latter is large enough to handle common scenarios64// but small enough not to drive up the footprint too much (several kb).65//66// We keep these buffers around once allocated, for subsequent requests. This67// means that by running the initialization early at a safe time - before68// any error happens - buffers can be pre-allocated. This increases the chance69// of useful callstacks in error scenarios in exchange for a some cycles spent70// at startup. This behavior can be controlled with -XX:+InitializeDbgHelpEarly71// and is off by default.7273///////7475// A simple buffer which attempts to allocate an optimal size but will76// fall back to a static minimally sized array on allocation error.77template <class T, int MINIMAL_CAPACITY, int OPTIMAL_CAPACITY>78class SimpleBufferWithFallback {79T _fallback_buffer[MINIMAL_CAPACITY];80T* _p;81int _capacity;8283// A sentinel at the end of the buffer to catch overflows.84void imprint_sentinel() {85assert(_p && _capacity > 0, "Buffer must be allocated");86_p[_capacity - 1] = (T)'X';87_capacity --;88}8990public:9192SimpleBufferWithFallback<T, MINIMAL_CAPACITY, OPTIMAL_CAPACITY> ()93: _p(NULL), _capacity(0)94{}9596// Note: no destructor because these buffers should, once97// allocated, live until process end.98// ~SimpleBufferWithFallback()99100// Note: We use raw ::malloc/::free here instead of os::malloc()/os::free101// to prevent circularities or secondary crashes during error reporting.102virtual void initialize () {103assert(_p == NULL && _capacity == 0, "Only call once.");104const size_t bytes = OPTIMAL_CAPACITY * sizeof(T);105T* q = (T*) ::malloc(bytes);106if (q != NULL) {107_p = q;108_capacity = OPTIMAL_CAPACITY;109} else {110_p = _fallback_buffer;111_capacity = (int)(sizeof(_fallback_buffer) / sizeof(T));112}113_p[0] = '\0';114imprint_sentinel();115}116117// We need a way to reset the buffer to fallback size for one special118// case, where two buffers need to be of identical capacity.119void reset_to_fallback_capacity() {120if (_p != _fallback_buffer) {121::free(_p);122}123_p = _fallback_buffer;124_capacity = (int)(sizeof(_fallback_buffer) / sizeof(T));125_p[0] = '\0';126imprint_sentinel();127}128129T* ptr() { return _p; }130const T* ptr() const { return _p; }131int capacity() const { return _capacity; }132133#ifdef ASSERT134void check() const {135assert(_p[_capacity] == (T)'X', "sentinel lost");136}137#else138void check() const {}139#endif140141};142143////144145// ModuleHandleArray: a list holding module handles. Needs to be large enough146// to hold one handle per loaded DLL.147// Note: a standard OpenJDK loads normally ~30 libraries, including system148// libraries, without third party libraries.149150typedef SimpleBufferWithFallback <HMODULE, 48, 512> ModuleHandleArrayBase;151152class ModuleHandleArray : public ModuleHandleArrayBase {153154int _num; // Number of handles in this array (may be < capacity).155156public:157158void initialize() {159ModuleHandleArrayBase::initialize();160_num = 0;161}162163int num() const { return _num; }164void set_num(int n) {165assert(n <= capacity(), "Too large");166_num = n;167}168169// Compare with another list; returns true if all handles are equal (incl.170// sort order)171bool equals(const ModuleHandleArray& other) const {172if (_num != other._num) {173return false;174}175if (::memcmp(ptr(), other.ptr(), _num * sizeof(HMODULE)) != 0) {176return false;177}178return true;179}180181// Copy content from other list.182void copy_content_from(ModuleHandleArray& other) {183assert(capacity() == other.capacity(), "Different capacities.");184memcpy(ptr(), other.ptr(), other._num * sizeof(HMODULE));185_num = other._num;186}187188};189190////191192// PathBuffer: a buffer to hold and work with a pdb search PATH - a concatenation193// of multiple directories separated by ';'.194// A single directory name can be (NTFS) as long as 32K, but in reality is195// seldom larger than the (historical) MAX_PATH of 260.196197#define MINIMUM_PDB_PATH_LENGTH MAX_PATH * 4198#define OPTIMAL_PDB_PATH_LENGTH MAX_PATH * 64199200typedef SimpleBufferWithFallback<char, MINIMUM_PDB_PATH_LENGTH, OPTIMAL_PDB_PATH_LENGTH> PathBufferBase;201202class PathBuffer: public PathBufferBase {203public:204205// Search PDB path for a directory. Search is case insensitive. Returns206// true if directory was found in the path, false otherwise.207bool contains_directory(const char* directory) {208if (ptr() == NULL) {209return false;210}211const size_t len = strlen(directory);212if (len == 0) {213return false;214}215char* p = ptr();216for(;;) {217char* q = strchr(p, ';');218if (q != NULL) {219if (len == (q - p)) {220if (strnicmp(p, directory, len) == 0) {221return true;222}223}224p = q + 1;225} else {226// tail227return stricmp(p, directory) == 0 ? true : false;228}229}230return false;231}232233// Appends the given directory to the path. Returns false if internal234// buffer size was not sufficient.235bool append_directory(const char* directory) {236const size_t len = strlen(directory);237if (len == 0) {238return false;239}240char* p = ptr();241const size_t len_now = strlen(p);242const size_t needs_capacity = len_now + 1 + len + 1; // xxx;yy\0243if (needs_capacity > (size_t)capacity()) {244return false; // OOM245}246if (len_now > 0) { // Not the first path element.247p += len_now;248*p = ';';249p ++;250}251strcpy(p, directory);252return true;253}254255};256257// A simple buffer to hold one single file name. A file name can be (NTFS) as258// long as 32K, but in reality is seldom larger than MAX_PATH.259typedef SimpleBufferWithFallback<char, MAX_PATH, 8 * K> FileNameBuffer;260261// A buffer to hold a C++ symbol. Usually small, but symbols may be larger for262// templates.263#define MINIMUM_SYMBOL_NAME_LEN 128264#define OPTIMAL_SYMBOL_NAME_LEN 1024265266typedef SimpleBufferWithFallback<uint8_t,267sizeof(IMAGEHLP_SYMBOL64) + MINIMUM_SYMBOL_NAME_LEN,268sizeof(IMAGEHLP_SYMBOL64) + OPTIMAL_SYMBOL_NAME_LEN> SymbolBuffer;269270static struct {271272// Two buffers to hold lists of loaded modules. handles across invocations of273// SymbolEngine::recalc_search_path().274ModuleHandleArray loaded_modules;275ModuleHandleArray last_loaded_modules;276// Buffer to retrieve and assemble the pdb search path.277PathBuffer search_path;278// Buffer to retrieve directory names for loaded modules.279FileNameBuffer dir_name;280// Buffer to retrieve decoded symbol information (in SymbolEngine::decode)281SymbolBuffer decode_buffer;282283void initialize() {284search_path.initialize();285dir_name.initialize();286decode_buffer.initialize();287288loaded_modules.initialize();289last_loaded_modules.initialize();290291// Note: both module lists must have the same capacity. If one allocation292// did fail, let them both fall back to the fallback size.293if (loaded_modules.capacity() != last_loaded_modules.capacity()) {294loaded_modules.reset_to_fallback_capacity();295last_loaded_modules.reset_to_fallback_capacity();296}297298assert(search_path.capacity() > 0 && dir_name.capacity() > 0 &&299decode_buffer.capacity() > 0 && loaded_modules.capacity() > 0 &&300last_loaded_modules.capacity() > 0, "Init error.");301}302303} g_buffers;304305306// Scan the loaded modules.307//308// For each loaded module, add the directory it is located in to the pdb search309// path, but avoid duplicates. Prior search path content is preserved.310//311// If p_search_path_was_updated is not NULL, points to a bool which, upon312// successful return from the function, contains true if the search path313// was updated, false if no update was needed because no new DLLs were314// loaded or unloaded.315//316// Returns true for success, false for error.317static bool recalc_search_path_locked(bool* p_search_path_was_updated) {318319if (p_search_path_was_updated) {320*p_search_path_was_updated = false;321}322323HANDLE hProcess = ::GetCurrentProcess();324325BOOL success = false;326327// 1) Retrieve current set search path.328// (PDB search path is a global setting and someone might have modified329// it, so take care not to remove directories, just to add our own).330331if (!WindowsDbgHelp::symGetSearchPath(hProcess, g_buffers.search_path.ptr(),332(int)g_buffers.search_path.capacity())) {333return false;334}335DEBUG_ONLY(g_buffers.search_path.check();)336337// 2) Retrieve list of modules handles of all currently loaded modules.338DWORD bytes_needed = 0;339const DWORD buffer_capacity_bytes = (DWORD)g_buffers.loaded_modules.capacity() * sizeof(HMODULE);340success = ::EnumProcessModules(hProcess, g_buffers.loaded_modules.ptr(),341buffer_capacity_bytes, &bytes_needed);342DEBUG_ONLY(g_buffers.loaded_modules.check();)343344// Note: EnumProcessModules is sloppily defined in terms of whether a345// too-small output buffer counts as error. Will it truncate but still346// return TRUE? Nobody knows and the manpage is not telling. So we count347// truncation it as error, disregarding the return value.348if (!success || bytes_needed > buffer_capacity_bytes) {349return false;350} else {351const int num_modules = bytes_needed / sizeof(HMODULE);352g_buffers.loaded_modules.set_num(num_modules);353}354355// Compare the list of module handles with the last list. If the lists are356// identical, no additional dlls were loaded and we can stop.357if (g_buffers.loaded_modules.equals(g_buffers.last_loaded_modules)) {358return true;359} else {360// Remember the new set of module handles and continue.361g_buffers.last_loaded_modules.copy_content_from(g_buffers.loaded_modules);362}363364// 3) For each loaded module: retrieve directory from which it was loaded.365// Add directory to search path (but avoid duplicates).366367bool did_modify_searchpath = false;368369for (int i = 0; i < (int)g_buffers.loaded_modules.num(); i ++) {370371const HMODULE hMod = g_buffers.loaded_modules.ptr()[i];372char* const filebuffer = g_buffers.dir_name.ptr();373const int file_buffer_capacity = g_buffers.dir_name.capacity();374const int len_returned = (int)::GetModuleFileName(hMod, filebuffer, (DWORD)file_buffer_capacity);375DEBUG_ONLY(g_buffers.dir_name.check();)376if (len_returned == 0) {377// This may happen when a module gets unloaded after our call to EnumProcessModules.378// It should be rare but may sporadically happen. Just ignore and continue with the379// next module.380continue;381} else if (len_returned == file_buffer_capacity) {382// Truncation. Just skip this module and continue with the next module.383continue;384}385386// Cut file name part off.387char* last_slash = ::strrchr(filebuffer, '\\');388if (last_slash == NULL) {389last_slash = ::strrchr(filebuffer, '/');390}391if (last_slash) {392*last_slash = '\0';393}394395// If this is already part of the search path, ignore it, otherwise396// append to search path.397if (!g_buffers.search_path.contains_directory(filebuffer)) {398if (!g_buffers.search_path.append_directory(filebuffer)) {399return false; // oom400}401DEBUG_ONLY(g_buffers.search_path.check();)402did_modify_searchpath = true;403}404405} // for each loaded module.406407// If we did not modify the search path, nothing further needs to be done.408if (!did_modify_searchpath) {409return true;410}411412// Set the search path to its new value.413if (!WindowsDbgHelp::symSetSearchPath(hProcess, g_buffers.search_path.ptr())) {414return false;415}416417if (p_search_path_was_updated) {418*p_search_path_was_updated = true;419}420421return true;422423}424425static bool demangle_locked(const char* symbol, char *buf, int buflen) {426427return WindowsDbgHelp::unDecorateSymbolName(symbol, buf, buflen, UNDNAME_COMPLETE) > 0;428429}430431static bool decode_locked(const void* addr, char* buf, int buflen, int* offset, bool do_demangle) {432433assert(g_buffers.decode_buffer.capacity() >= (sizeof(IMAGEHLP_SYMBOL64) + MINIMUM_SYMBOL_NAME_LEN),434"Decode buffer too small.");435assert(buf != NULL && buflen > 0 && offset != NULL, "invalid output buffer.");436437DWORD64 displacement;438PIMAGEHLP_SYMBOL64 pSymbol = NULL;439bool success = false;440441pSymbol = (PIMAGEHLP_SYMBOL64) g_buffers.decode_buffer.ptr();442pSymbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);443pSymbol->MaxNameLength = (DWORD)(g_buffers.decode_buffer.capacity() - sizeof(IMAGEHLP_SYMBOL64) - 1);444445// It is unclear how SymGetSymFromAddr64 handles truncation. Experiments446// show it will return TRUE but not zero terminate (which is a really bad447// combination). Lets be super careful.448::memset(pSymbol->Name, 0, pSymbol->MaxNameLength); // To catch truncation.449450if (WindowsDbgHelp::symGetSymFromAddr64(::GetCurrentProcess(), (DWORD64)addr, &displacement, pSymbol)) {451success = true;452if (pSymbol->Name[pSymbol->MaxNameLength - 1] != '\0') {453// Symbol was truncated. Do not attempt to demangle. Instead, zero terminate the454// truncated string. We still return success - the truncated string may still455// be usable for the caller.456pSymbol->Name[pSymbol->MaxNameLength - 1] = '\0';457do_demangle = false;458}459460// Attempt to demangle.461if (do_demangle && demangle_locked(pSymbol->Name, buf, buflen)) {462// ok.463} else {464::strncpy(buf, pSymbol->Name, buflen - 1);465}466buf[buflen - 1] = '\0';467468*offset = (int)displacement;469}470471DEBUG_ONLY(g_buffers.decode_buffer.check();)472473return success;474}475476static enum {477state_uninitialized = 0,478state_ready = 1,479state_error = 2480} g_state = state_uninitialized;481482static void initialize() {483484assert(g_state == state_uninitialized, "wrong sequence");485g_state = state_error;486487// 1) Initialize buffers.488g_buffers.initialize();489490// 1) Call SymInitialize491HANDLE hProcess = ::GetCurrentProcess();492WindowsDbgHelp::symSetOptions(SYMOPT_FAIL_CRITICAL_ERRORS | SYMOPT_DEFERRED_LOADS |493SYMOPT_EXACT_SYMBOLS | SYMOPT_LOAD_LINES);494if (!WindowsDbgHelp::symInitialize(hProcess, NULL, TRUE)) {495return;496}497498// Note: we ignore any errors from this point on. The symbol engine may be499// usable enough.500g_state = state_ready;501502(void)recalc_search_path_locked(NULL);503504}505506///////////////////// External functions //////////////////////////507508// All outside facing functions are synchronized. Also, we run509// initialization on first touch.510511static CRITICAL_SECTION g_cs;512513namespace { // Do not export.514class SymbolEngineEntry {515public:516SymbolEngineEntry() {517::EnterCriticalSection(&g_cs);518if (g_state == state_uninitialized) {519initialize();520}521}522~SymbolEngineEntry() {523::LeaveCriticalSection(&g_cs);524}525};526}527528// Called at DLL_PROCESS_ATTACH.529void SymbolEngine::pre_initialize() {530::InitializeCriticalSection(&g_cs);531}532533bool SymbolEngine::decode(const void* addr, char* buf, int buflen, int* offset, bool do_demangle) {534535assert(buf != NULL && buflen > 0 && offset != NULL, "Argument error");536buf[0] = '\0';537*offset = -1;538539if (addr == NULL) {540return false;541}542543SymbolEngineEntry entry_guard;544545// Try decoding the symbol once. If we fail, attempt to rebuild the546// symbol search path - maybe the pc points to a dll whose pdb file is547// outside our search path. Then do attempt the decode again.548bool success = decode_locked(addr, buf, buflen, offset, do_demangle);549if (!success) {550bool did_update_search_path = false;551if (recalc_search_path_locked(&did_update_search_path)) {552if (did_update_search_path) {553success = decode_locked(addr, buf, buflen, offset, do_demangle);554}555}556}557558return success;559560}561562bool SymbolEngine::demangle(const char* symbol, char *buf, int buflen) {563564SymbolEngineEntry entry_guard;565566return demangle_locked(symbol, buf, buflen);567568}569570bool SymbolEngine::recalc_search_path(bool* p_search_path_was_updated) {571572SymbolEngineEntry entry_guard;573574return recalc_search_path_locked(p_search_path_was_updated);575576}577578bool SymbolEngine::get_source_info(const void* addr, char* buf, size_t buflen,579int* line_no)580{581assert(buf != NULL && buflen > 0 && line_no != NULL, "Argument error");582buf[0] = '\0';583*line_no = -1;584585if (addr == NULL) {586return false;587}588589SymbolEngineEntry entry_guard;590591IMAGEHLP_LINE64 lineinfo;592memset(&lineinfo, 0, sizeof(lineinfo));593lineinfo.SizeOfStruct = sizeof(lineinfo);594DWORD displacement;595if (WindowsDbgHelp::symGetLineFromAddr64(::GetCurrentProcess(), (DWORD64)addr,596&displacement, &lineinfo)) {597if (buf != NULL && buflen > 0 && lineinfo.FileName != NULL) {598// We only return the file name, not the whole path.599char* p = lineinfo.FileName;600char* q = strrchr(lineinfo.FileName, '\\');601if (q) {602p = q + 1;603}604::strncpy(buf, p, buflen - 1);605buf[buflen - 1] = '\0';606}607if (line_no != 0) {608*line_no = lineinfo.LineNumber;609}610return true;611}612return false;613}614615// Print one liner describing state (if library loaded, which functions are616// missing - if any, and the dbhelp API version)617void SymbolEngine::print_state_on(outputStream* st) {618619SymbolEngineEntry entry_guard;620621st->print("symbol engine: ");622623if (g_state == state_uninitialized) {624st->print("uninitialized.");625} else if (g_state == state_error) {626st->print("initialization error.");627} else {628st->print("initialized successfully");629st->print(" - sym options: 0x%X", WindowsDbgHelp::symGetOptions());630st->print(" - pdb path: ");631if (WindowsDbgHelp::symGetSearchPath(::GetCurrentProcess(),632g_buffers.search_path.ptr(),633(int)g_buffers.search_path.capacity())) {634st->print_raw(g_buffers.search_path.ptr());635} else {636st->print_raw("(cannot be retrieved)");637}638}639st->cr();640641}642643644