diff --git a/SystemInformation.cxx b/SystemInformation.cxx index 96dff616cc..5db2be239b 100644 --- a/SystemInformation.cxx +++ b/SystemInformation.cxx @@ -3981,12 +3981,18 @@ std::string SystemInformationImplementation::GetProgramStack(int firstFrame, void* stack[TRACE_MAX_STACK_FRAMES]; HANDLE process = GetCurrentProcess(); + // SymSetOptions affects the process-global symbol handler options, so + // save the caller's options and restore them before returning. + DWORD options = SymGetOptions(); + SymSetOptions(options | SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | + SYMOPT_LOAD_LINES); SymInitialize(process, nullptr, TRUE); WORD numberOfFrames = CaptureStackBackTrace(firstFrame, TRACE_MAX_STACK_FRAMES, stack, nullptr); SYMBOL_INFO* symbol = static_cast( - malloc(sizeof(SYMBOL_INFO) + - (TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR))); + calloc(1, + sizeof(SYMBOL_INFO) + + (TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR))); symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH; symbol->SizeOfStruct = sizeof(SYMBOL_INFO); DWORD displacement; @@ -3994,15 +4000,59 @@ std::string SystemInformationImplementation::GetProgramStack(int firstFrame, line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); for (int i = 0; i < numberOfFrames; i++) { DWORD64 address = reinterpret_cast(stack[i]); - SymFromAddr(process, address, nullptr, symbol); - if (SymGetLineFromAddr64(process, address, &displacement, &line)) { + DWORD64 symDisplacement = 0; + bool haveName = + SymFromAddr(process, address, &symDisplacement, symbol) != FALSE; + DWORD64 moduleBase = SymGetModuleBase64(process, address); + // When a module has no matching PDB, dbghelp falls back to its export + // table and reports the nearest preceding export, which is generally not + // the (non-exported) function the address really belongs to. Names are + // only exact when real debug info was loaded for the module, so ask the + // module how its symbols were obtained rather than trusting the name. + // Inexact names are still printed, but marked and always accompanied by + // the module-relative address so they cannot be mistaken for the truth. + bool exactName = haveName; + if (exactName) { + IMAGEHLP_MODULE64 moduleInfo; + memset(&moduleInfo, 0, sizeof(moduleInfo)); + moduleInfo.SizeOfStruct = sizeof(moduleInfo); + if (!SymGetModuleInfo64(process, address, &moduleInfo) || + moduleInfo.SymType == SymNone || moduleInfo.SymType == SymExport || + moduleInfo.SymType == SymDeferred) { + exactName = false; + } + } + if (exactName && + SymGetLineFromAddr64(process, address, &displacement, &line)) { oss << " at " << symbol->Name << " in " << line.FileName << " line " << line.LineNumber << std::endl; + } else if (exactName) { + oss << " at " << symbol->Name << "+0x" << std::hex << symDisplacement + << std::dec << std::endl; } else { - oss << " at " << symbol->Name << std::endl; + // Report the module containing the address together with the offset + // into that module, which is what a disassembler needs, plus the + // nearest export as an approximate location hint when one is known. + wchar_t modulePath[MAX_PATH]; + DWORD64 moduleBase = SymGetModuleBase64(process, address); + if (moduleBase && + GetModuleFileNameW(reinterpret_cast(moduleBase), modulePath, + MAX_PATH) > 0) { + oss << " at " << modulePath << "+0x" << std::hex + << (address - moduleBase) << std::dec; + } else { + oss << " at 0x" << std::hex << address << std::dec; + } + if (haveName) { + oss << " (near " << symbol->Name << "+0x" << std::hex + << symDisplacement << std::dec << ")"; + } + oss << std::endl; } } free(symbol); + SymSetOptions(options); + SymCleanup(process); #else programStack += "" diff --git a/SystemTools.cxx b/SystemTools.cxx index b260e38701..6f9c2430f2 100644 --- a/SystemTools.cxx +++ b/SystemTools.cxx @@ -298,7 +298,7 @@ static time_t windows_filetime_to_posix_time(const FILETIME& ft) typedef KWSYS_NAMESPACE::SystemTools::mode_t mode_t; # endif -inline int Mkdir(std::string const& dir, mode_t const* mode) +static inline int Mkdir(std::string const& dir, mode_t const* mode) { int ret = _wmkdir(KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str()); @@ -306,12 +306,12 @@ inline int Mkdir(std::string const& dir, mode_t const* mode) KWSYS_NAMESPACE::SystemTools::SetPermissions(dir, *mode); return ret; } -inline int Rmdir(std::string const& dir) +static inline int Rmdir(std::string const& dir) { return _wrmdir( KWSYS_NAMESPACE::Encoding::ToWindowsExtendedPath(dir).c_str()); } -inline char const* Getcwd(char* buf, unsigned int len) +static inline char const* Getcwd(char* buf, unsigned int len) { std::vector w_buf(len); if (_wgetcwd(&w_buf[0], len)) { @@ -329,15 +329,16 @@ inline char const* Getcwd(char* buf, unsigned int len) } return 0; } -inline int Chdir(std::string const& dir) +static inline int Chdir(std::string const& dir) { // We cannot use ToWindowsExtendedPath here because that causes a // UNC path to be recorded as the process working directory, and // can break child processes. return _wchdir(KWSYS_NAMESPACE::Encoding::ToWide(dir).c_str()); } -inline void Realpath(std::string const& path, std::string& resolved_path, - std::string* errorMessage = nullptr) +static inline void Realpath(std::string const& path, + std::string& resolved_path, + std::string* errorMessage = nullptr) { std::wstring tmp = KWSYS_NAMESPACE::Encoding::ToWide(path); wchar_t fullpath[MAX_PATH]; @@ -372,25 +373,26 @@ inline void Realpath(std::string const& path, std::string& resolved_path, # include # include -inline int Mkdir(std::string const& dir, mode_t const* mode) +static inline int Mkdir(std::string const& dir, mode_t const* mode) { return mkdir(dir.c_str(), mode ? *mode : 00777); } -inline int Rmdir(std::string const& dir) +static inline int Rmdir(std::string const& dir) { return rmdir(dir.c_str()); } -inline char const* Getcwd(char* buf, unsigned int len) +static inline char const* Getcwd(char* buf, unsigned int len) { return getcwd(buf, len); } -inline int Chdir(std::string const& dir) +static inline int Chdir(std::string const& dir) { return chdir(dir.c_str()); } -inline void Realpath(std::string const& path, std::string& resolved_path, - std::string* errorMessage = nullptr) +static inline void Realpath(std::string const& path, + std::string& resolved_path, + std::string* errorMessage = nullptr) { char resolved_name[KWSYS_SYSTEMTOOLS_MAXPATH];