diff --git a/public/client/TracyCallstack.cpp b/public/client/TracyCallstack.cpp index bd66783210..08c46dc592 100644 --- a/public/client/TracyCallstack.cpp +++ b/public/client/TracyCallstack.cpp @@ -8,6 +8,7 @@ #include "TracyStringHelpers.hpp" #include "../common/TracyAlloc.hpp" #include "../common/TracySystem.hpp" +#include "TracySymbols.hpp" #ifdef TRACY_HAS_CALLSTACK @@ -18,16 +19,7 @@ # endif # include # include -# include -# ifdef _MSC_VER -# pragma warning( push ) -# pragma warning( disable : 4091 ) -# endif -# include -# pragma comment( lib, "dbghelp.lib" ) -# ifdef _MSC_VER -# pragma warning( pop ) -# endif +# include "TracySymbolsDbgHelp.cpp" #elif defined(TRACY_USE_LIBBACKTRACE) # include "../libbacktrace/backtrace.hpp" @@ -113,226 +105,11 @@ extern "C" const char* ___tracy_demangle( const char* mangled ) #endif #endif -#if defined(TRACY_USE_LIBBACKTRACE) && TRACY_HAS_CALLSTACK != 4 // dl_iterate_phdr is required for the current image cache. Need to move it to libbacktrace? -# define TRACY_HAS_DL_ITERATE_PHDR_TO_REFRESH_IMAGE_CACHE -# include -#endif - namespace tracy { - -static bool IsKernelAddress(uint64_t addr) { - return (addr >> 63) != 0; -} - -void DestroyImageEntry( ImageEntry& entry ) -{ - tracy_free( entry.m_path ); - tracy_free( entry.m_name ); -} - -class ImageCache -{ -public: - - ImageCache( size_t imageCacheCapacity = 512 ) - : m_images( imageCacheCapacity ) - { - } - - ~ImageCache() - { - Clear(); - } - - ImageEntry* AddEntry( const ImageEntry& entry ) - { - if( m_sorted ) m_sorted = m_images.empty() || ( entry.m_startAddress < m_images.back().m_startAddress ); - ImageEntry* newEntry = m_images.push_next(); - *newEntry = entry; - return newEntry; - } - - const ImageEntry* GetImageForAddress( uint64_t address ) - { - Sort(); - - auto it = std::lower_bound( m_images.begin(), m_images.end(), address, - []( const ImageEntry& lhs, const uint64_t rhs ) { return lhs.m_startAddress > rhs; } ); - - if( it != m_images.end() && address < it->m_endAddress ) - { - return it; - } - return nullptr; - } - - void Sort() - { - if( m_sorted ) return; - - std::sort( m_images.begin(), m_images.end(), - []( const ImageEntry& lhs, const ImageEntry& rhs ) { return lhs.m_startAddress > rhs.m_startAddress; } ); - m_sorted = true; - } - - void Clear() - { - for( ImageEntry& entry : m_images ) - { - DestroyImageEntry( entry ); - } - - m_sorted = true; - m_images.clear(); - } - - bool ContainsImage( uint64_t startAddress ) const - { - return std::any_of( m_images.begin(), m_images.end(), [startAddress]( const ImageEntry& entry ) { return startAddress == entry.m_startAddress; } ); - } -protected: - tracy::FastVector m_images; - bool m_sorted = true; -}; - -#ifdef TRACY_HAS_DL_ITERATE_PHDR_TO_REFRESH_IMAGE_CACHE -// when we have access to dl_iterate_phdr(), we can build a cache of address ranges to image paths -// so we can quickly determine which image an address falls into. -// We refresh this cache only when we hit an address that doesn't fall into any known range. -class ImageCacheDlIteratePhdr : public ImageCache -{ -public: - - ImageCacheDlIteratePhdr() - { - Refresh(); - } - - ~ImageCacheDlIteratePhdr() - { - } - - const ImageEntry* GetImageForAddress( uint64_t address ) - { - const ImageEntry* entry = ImageCache::GetImageForAddress( address ); - if( !entry ) - { - Refresh(); - return ImageCache::GetImageForAddress( address ); - } - return entry; - } - -private: - bool m_updated = false; - bool m_haveMainImageName = false; - - static int Callback( struct dl_phdr_info* info, size_t size, void* data ) - { - ImageCacheDlIteratePhdr* cache = reinterpret_cast( data ); - - const auto startAddress = static_cast( info->dlpi_addr ); - if( cache->ContainsImage( startAddress ) ) return 0; - - const uint32_t headerCount = info->dlpi_phnum; - assert( headerCount > 0); - const auto endAddress = static_cast( info->dlpi_addr + - info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr + info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz); - - ImageEntry image{}; - image.m_startAddress = startAddress; - image.m_endAddress = endAddress; - - // the base executable name isn't provided when iterating with dl_iterate_phdr, - // we will have to patch the executable image name outside this callback - image.m_name = info->dlpi_name && info->dlpi_name[0] != '\0' ? CopyStringFast( info->dlpi_name ) : nullptr; - - cache->AddEntry( image ); - cache->m_updated = true; - - return 0; - } - - void Refresh() - { - m_updated = false; - dl_iterate_phdr( Callback, this ); - - if( m_updated ) - { - Sort(); - // patch the main executable image name here, as calling dl_* functions inside the dl_iterate_phdr callback might cause deadlocks - UpdateMainImageName(); - } - } - - void UpdateMainImageName() - { - if( m_haveMainImageName ) - { - return; - } - - for( ImageEntry& entry : m_images ) - { - if( entry.m_name == nullptr ) - { - Dl_info dlInfo; - if( dladdr( (void *)entry.m_startAddress, &dlInfo ) ) - { - if( dlInfo.dli_fname ) - { - entry.m_name = CopyString( dlInfo.dli_fname ); - } - } - - // we only expect one entry to be null for the main executable entry - break; - } - } - - m_haveMainImageName = true; - } - void Clear() - { - ImageCache::Clear(); - m_haveMainImageName = false; - } -}; -using UserlandImageCache = ImageCacheDlIteratePhdr; -#else -using UserlandImageCache = ImageCache; -#endif //#ifdef TRACY_HAS_DL_ITERATE_PHDR_TO_REFRESH_IMAGE_CACHE - -static UserlandImageCache* s_imageCache; -static ImageCache* s_krnlCache; - -void CreateImageCaches() -{ - assert( s_imageCache == nullptr && s_krnlCache == nullptr ); - s_imageCache = new ( tracy_malloc( sizeof( UserlandImageCache ) ) ) UserlandImageCache(); - s_krnlCache = new ( tracy_malloc( sizeof( ImageCache ) ) ) ImageCache(); -} - -void DestroyImageCaches() -{ - if( s_krnlCache != nullptr ) - { - s_krnlCache->~ImageCache(); - tracy_free( s_krnlCache ); - s_krnlCache = nullptr; - } - - if( s_imageCache != nullptr ) - { - s_imageCache->~UserlandImageCache(); - tracy_free( s_imageCache ); - s_imageCache = nullptr; - } - -} - +// TODO: move to TracySymbols.cpp +UserlandImageCache* s_imageCache; +ImageCache* s_krnlCache; #ifdef __linux__ @@ -560,38 +337,14 @@ void InitExternalImageCache( pid_t pid ) #endif // __linux__ -// when "TRACY_SYMBOL_OFFLINE_RESOLVE" is set, instead of fully resolving symbols at runtime, -// simply resolve the offset and image name (which will be enough the resolving to be done offline) -#ifdef TRACY_SYMBOL_OFFLINE_RESOLVE -constexpr bool s_shouldResolveSymbolsOffline = true; -#else -static bool s_shouldResolveSymbolsOffline = false; -bool ShouldResolveSymbolsOffline() -{ - const char* symbolOfflineResolve = GetEnvVar( "TRACY_SYMBOL_OFFLINE_RESOLVE" ); - return (symbolOfflineResolve && symbolOfflineResolve[0] == '1'); -} +#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE +bool s_shouldResolveSymbolsOffline = false; #endif // #ifdef TRACY_SYMBOL_OFFLINE_RESOLVE #if TRACY_HAS_CALLSTACK == 1 -constexpr size_t MaxCbTrace = 64; -constexpr size_t MaxNameSize = 8*1024; - -int cb_num; -CallstackEntry cb_data[MaxCbTrace]; - extern "C" { - typedef DWORD (__stdcall *t_SymAddrIncludeInlineTrace)( HANDLE hProcess, DWORD64 Address ); - typedef BOOL (__stdcall *t_SymQueryInlineTrace)( HANDLE hProcess, DWORD64 StartAddress, DWORD StartContext, DWORD64 StartRetAddress, DWORD64 CurAddress, LPDWORD CurContext, LPDWORD CurFrameIndex ); - typedef BOOL (__stdcall *t_SymFromInlineContext)( HANDLE hProcess, DWORD64 Address, ULONG InlineContext, PDWORD64 Displacement, PSYMBOL_INFO Symbol ); - typedef BOOL (__stdcall *t_SymGetLineFromInlineContext)( HANDLE hProcess, DWORD64 qwAddr, ULONG InlineContext, DWORD64 qwModuleBaseAddress, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64 ); - - t_SymAddrIncludeInlineTrace _SymAddrIncludeInlineTrace = 0; - t_SymQueryInlineTrace _SymQueryInlineTrace = 0; - t_SymFromInlineContext _SymFromInlineContext = 0; - t_SymGetLineFromInlineContext _SymGetLineFromInlineContext = 0; typedef unsigned long (__stdcall *___tracy_t_RtlWalkFrameChain)( void**, unsigned long, unsigned long ); ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChainPtr = nullptr; @@ -606,201 +359,6 @@ void InitCallstackCritical() ___tracy_RtlWalkFrameChainPtr = (___tracy_t_RtlWalkFrameChain)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlWalkFrameChain" ); } -static void SymError( const char* function, DWORD code ) { - char message[1024] = {}; - int written = snprintf( message, sizeof( message ), "ERROR: %s FAILED with code %u (0x%x) | ", function, code, code ); - written += FormatMessageA( - FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - code, - MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), - (LPSTR)&message[written], - sizeof(message) - written, - NULL - ); - fprintf( stderr, "%s\n", message ); - OutputDebugStringA( message ); -} - -void DbgHelpInit() -{ - if( s_shouldResolveSymbolsOffline ) return; - - _SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymAddrIncludeInlineTrace"); - _SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymQueryInlineTrace"); - _SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymFromInlineContext"); - _SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymGetLineFromInlineContext"); - -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_INIT; - DBGHELP_LOCK; -#endif - - // append executable path to the _NT_SYMBOL_PATH environment variable - char buffer [32767]; // max env var length on Windows (including null-terminator) - DWORD length = GetEnvironmentVariableA( "_NT_SYMBOL_PATH", buffer, sizeof( buffer ) ); - if( length > sizeof( buffer ) ) SymError( "GetEnvironmentVariableA", GetLastError() ); - else if( length + 1 >= sizeof( buffer ) ) SymError( "_TracyAppendEnvironmentVariable", ERROR_INSUFFICIENT_BUFFER ); - else - { - buffer[length] = ';'; - buffer[++length] = '\0'; - length += GetModuleFileNameA( NULL, &buffer[length], sizeof( buffer ) - length ); - if( length >= sizeof( buffer ) && GetLastError() == ERROR_INSUFFICIENT_BUFFER ) - { - SymError( "GetModuleFileNameA", GetLastError() ); - } - else - { - while( length > 0 && buffer[--length] != '\\' ) - buffer[length] = '\0'; - } - } - - assert( length < sizeof( buffer ) ); - if( SetEnvironmentVariableA( "_NT_SYMBOL_PATH", buffer ) == FALSE ) SymError( "SetEnvironmentVariableA", GetLastError() ); - - SymSetOptions( SymGetOptions() | SYMOPT_LOAD_LINES ); - if( SymInitialize( GetCurrentProcess(), NULL, TRUE ) == FALSE ) - { - SymError( "SymInitialize", GetLastError() ); - } - else if( GetModuleHandleA( "SymSrv.dll" ) == NULL ) - { - TracyDebug( "SymSrv.dll was not loaded, it needs to be near a matching version of DbgHelp.dll. Symbol resolution may fail as symbol servers will not be used. See https://learn.microsoft.com/en-us/windows/win32/debug/calling-the-dbghelp-library" ); - } - - -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_UNLOCK; -#endif -} - -DWORD64 DbgHelpLoadSymbolsForModule( const char* imageName, uint64_t baseOfDll, uint32_t bllSize ) -{ - if( s_shouldResolveSymbolsOffline ) return 0; - return SymLoadModuleEx( GetCurrentProcess(), nullptr, imageName, nullptr, baseOfDll, bllSize, nullptr, 0 ); -} - -char* FormatImageName( const char* imageName, uint32_t imageNameLength ) -{ - // when doing offline symbol resolution, we must store the full path of the dll for the resolving to work - if( s_shouldResolveSymbolsOffline ) - { - return CopyStringFast( imageName, imageNameLength ); - } - else - { - const char* ptr = imageName + imageNameLength; - while( ptr > imageName && *ptr != '\\' && *ptr != '/' ) ptr--; - if( ptr > imageName ) ptr++; - const auto namelen = imageName + imageNameLength - ptr; - - char* alloc = (char*)tracy_malloc_fast( namelen + 3 ); - alloc[0] = '['; - memcpy( alloc + 1, ptr, namelen ); - alloc[namelen + 1] = ']'; - alloc[namelen + 2] = '\0'; - return alloc; - } -} - -ImageEntry* CacheModuleInfo( const char* imagePath, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) -{ - ImageEntry moduleEntry = {}; - moduleEntry.m_startAddress = baseOfDll; - moduleEntry.m_endAddress = baseOfDll + dllSize; - moduleEntry.m_path = CopyStringFast( imagePath, imageNameLength ); - moduleEntry.m_name = FormatImageName( imagePath, imageNameLength ); - - return s_imageCache->AddEntry( moduleEntry ); -} - -ImageEntry* LoadSymbolsForModuleAndCache( const char* imagePath, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) -{ - DbgHelpLoadSymbolsForModule( imagePath, baseOfDll, dllSize ); - return CacheModuleInfo( imagePath, imageNameLength, baseOfDll, dllSize ); -} - -static void CacheProcessDrivers() -{ - DWORD needed; - LPVOID dev[4096]; - if( EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 ) - { - char windir[MAX_PATH]; - if( !GetWindowsDirectoryA( windir, sizeof( windir ) ) ) memcpy( windir, "c:\\windows", 11 ); - const auto windirlen = strlen( windir ); - - const auto sz = needed / sizeof( LPVOID ); - for( size_t i=0; i", 2 ); - - ImageEntry kernelDriver{}; - kernelDriver.m_startAddress = (uint64_t)dev[i]; - kernelDriver.m_endAddress = 0; - kernelDriver.m_name = buf; - kernelDriver.m_path = nullptr; - - const auto len = GetDeviceDriverFileNameA( dev[i], fn, sizeof( fn ) ); - if( len != 0 ) - { - char full[MAX_PATH]; - char* path = fn; - - if( memcmp( fn, "\\SystemRoot\\", 12 ) == 0 ) - { - memcpy( full, windir, windirlen ); - strcpy( full + windirlen, fn + 11 ); - path = full; - } - - DbgHelpLoadSymbolsForModule( path, (DWORD64)dev[i], 0 ); - - kernelDriver.m_path = CopyString( path ); - } - - s_krnlCache->AddEntry(kernelDriver); - } - } - s_krnlCache->Sort(); - } -} - -static void CacheProcessModules() -{ - DWORD needed; - HANDLE proc = GetCurrentProcess(); - HMODULE mod[1024]; - if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 ) - { - const auto sz = needed / sizeof( HMODULE ); - for( size_t i=0; i 0 ) - { - // This may be a new module loaded since our call to SymInitialize. - // Just in case, force DbgHelp to load its pdb ! - LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); - } - } - } - } -} - void InitCallstack() { #ifndef TRACY_SYMBOL_OFFLINE_RESOLVE @@ -814,30 +372,6 @@ void InitCallstack() CreateImageCaches(); DbgHelpInit(); - -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_LOCK; -#endif - - // use TRACY_NO_DBGHELP_INIT_LOAD=1 to disable preloading of driver - // and process module symbol loading at startup time - they will be loaded on demand later - // Sometimes this process can take a very long time and prevent resolving callstack frames - // symbols during that time. - const char* noInitLoadEnv = GetEnvVar( "TRACY_NO_DBGHELP_INIT_LOAD" ); - const bool initTimeModuleLoad = !( noInitLoadEnv && noInitLoadEnv[0] == '1' ); - if ( !initTimeModuleLoad ) - { - TracyDebug( "TRACY: skipping init time dbghelper module load" ); - } - else - { - CacheProcessDrivers(); - CacheProcessModules(); - } - -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_UNLOCK; -#endif } void EndCallstack() @@ -845,268 +379,6 @@ void EndCallstack() DestroyImageCaches(); } -const char* DecodeCallstackPtrFast( uint64_t ptr ) -{ - if( s_shouldResolveSymbolsOffline ) return "[unresolved]"; - - static char ret[MaxNameSize]; - const auto proc = GetCurrentProcess(); - - char buf[sizeof( SYMBOL_INFO ) + MaxNameSize]; - auto si = (SYMBOL_INFO*)buf; - si->SizeOfStruct = sizeof( SYMBOL_INFO ); - si->MaxNameLen = MaxNameSize; - -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_LOCK; -#endif - if( SymFromAddr( proc, ptr, nullptr, si ) == 0 ) - { - *ret = '\0'; - } - else - { - memcpy( ret, si->Name, si->NameLen ); - ret[si->NameLen] = '\0'; - } -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_UNLOCK; -#endif - return ret; -} - -const char* GetKernelModulePath( uint64_t addr ) -{ - assert( IsKernelAddress( addr ) ); - if( !s_krnlCache ) return nullptr; - const ImageEntry* imageEntry = s_krnlCache->GetImageForAddress( addr ); - if( imageEntry ) return imageEntry->m_path; - return nullptr; -} - -struct ModuleNameAndBaseAddress -{ - const char* name; - uint64_t baseAddr; -}; - -ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols( uint64_t addr ) -{ - if( IsKernelAddress( addr ) ) - { - const ImageEntry* entry = s_krnlCache->GetImageForAddress( addr ); - if( entry != nullptr ) return ModuleNameAndBaseAddress{ entry->m_name, entry->m_startAddress }; - return ModuleNameAndBaseAddress{ "", addr }; - } - - const ImageEntry* entry = s_imageCache->GetImageForAddress( addr ); - if( entry != nullptr ) return ModuleNameAndBaseAddress{ entry->m_name, entry->m_startAddress }; - - HANDLE proc = GetCurrentProcess(); - // Do not use FreeLibrary because we set the flag GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT - // see https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexa to get more information - constexpr DWORD flag = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; - HMODULE mod = NULL; - - InitRpmalloc(); - if( GetModuleHandleExA( flag, (char*)addr, &mod ) != 0 ) - { - MODULEINFO info; - if( GetModuleInformation( proc, mod, &info, sizeof( info ) ) != 0 ) - { - const auto base = uint64_t( info.lpBaseOfDll ); - if( addr >= base && addr < ( base + info.SizeOfImage ) ) - { - char name[1024]; - const auto nameLength = GetModuleFileNameA( mod, name, sizeof( name ) ); - if( nameLength > 0 ) - { - // since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize) - ImageEntry* cachedModule = LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); - return ModuleNameAndBaseAddress{ cachedModule->m_name, cachedModule->m_startAddress }; - } - } - } - } - - return ModuleNameAndBaseAddress{ "[unknown]", 0x0 }; -} - -CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) -{ - CallstackSymbolData sym; - - if( s_shouldResolveSymbolsOffline ) - { - sym.file = "[unknown]"; - sym.line = 0; - sym.needFree = false; - return sym; - } - - IMAGEHLP_LINE64 line; - DWORD displacement = 0; - line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_LOCK; -#endif - const auto res = SymGetLineFromAddr64( GetCurrentProcess(), ptr, &displacement, &line ); - if( res == 0 || line.LineNumber >= 0xF00000 ) - { - sym.file = "[unknown]"; - sym.line = 0; - sym.needFree = false; - } - else - { - sym.file = CopyString( line.FileName ); - sym.line = line.LineNumber; - sym.needFree = true; - } -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_UNLOCK; -#endif - return sym; -} - -static CallstackEntryData MakeUnresolvedCallstackEntryData( uint64_t ptr, ModuleNameAndBaseAddress moduleNameAndBaseAddress ) -{ - cb_data[0].symAddr = ptr - moduleNameAndBaseAddress.baseAddr; - cb_data[0].symLen = 0; - - cb_data[0].name = CopyStringFast( "[unresolved]" ); - cb_data[0].file = CopyStringFast( "[unknown]" ); - cb_data[0].line = 0; - - return { cb_data, 1, moduleNameAndBaseAddress.name }; -} - -CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) -{ -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_LOCK; -#endif - - InitRpmalloc(); - - const ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols( ptr ); - - if( s_shouldResolveSymbolsOffline ) - { -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_UNLOCK; -#endif - return MakeUnresolvedCallstackEntryData( ptr, moduleNameAndAddress ); - } - - int write; - const auto proc = GetCurrentProcess(); - -#if !defined TRACY_NO_CALLSTACK_INLINES - BOOL doInline = FALSE; - DWORD ctx = 0; - DWORD inlineNum = 0; - if( _SymAddrIncludeInlineTrace ) - { - inlineNum = _SymAddrIncludeInlineTrace( proc, ptr ); - if( inlineNum > MaxCbTrace - 1 ) inlineNum = MaxCbTrace - 1; - DWORD idx; - if( inlineNum != 0 ) doInline = _SymQueryInlineTrace( proc, ptr, 0, ptr, ptr, &ctx, &idx ); - } - if( doInline ) - { - write = inlineNum; - cb_num = 1 + inlineNum; - } - else -#endif - { - write = 0; - cb_num = 1; - } - - char buf[sizeof( SYMBOL_INFO ) + MaxNameSize]; - auto si = (SYMBOL_INFO*)buf; - si->SizeOfStruct = sizeof( SYMBOL_INFO ); - si->MaxNameLen = MaxNameSize; - - const auto symValid = SymFromAddr( proc, ptr, nullptr, si ) != 0; - - IMAGEHLP_LINE64 line; - DWORD displacement = 0; - line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); - - { - const char* filename; - const auto res = SymGetLineFromAddr64( proc, ptr, &displacement, &line ); - if( res == 0 || line.LineNumber >= 0xF00000 ) - { - filename = "[unknown]"; - cb_data[write].line = 0; - } - else - { - filename = line.FileName; - cb_data[write].line = line.LineNumber; - } - - cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); - cb_data[write].file = CopyStringFast( filename ); - if( symValid ) - { - cb_data[write].symLen = si->Size; - cb_data[write].symAddr = si->Address; - } - else - { - cb_data[write].symLen = 0; - cb_data[write].symAddr = 0; - } - } - -#if !defined TRACY_NO_CALLSTACK_INLINES - if( doInline ) - { - for( DWORD i=0; iName, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); - cb.file = CopyStringFast( filename ); - if( symInlineValid ) - { - cb.symLen = si->Size; - cb.symAddr = si->Address; - } - else - { - cb.symLen = 0; - cb.symAddr = 0; - } - - ctx++; - } - } -#endif -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_UNLOCK; -#endif - - return { cb_data, uint8_t( cb_num ), moduleNameAndAddress.name }; -} - #elif defined(TRACY_USE_LIBBACKTRACE) constexpr size_t MaxCbTrace = 64; diff --git a/public/client/TracySymbols.hpp b/public/client/TracySymbols.hpp new file mode 100644 index 0000000000..19a9dcacbe --- /dev/null +++ b/public/client/TracySymbols.hpp @@ -0,0 +1,252 @@ +#ifndef __TRACYSYMBOLS_HPP__ +#define __TRACYSYMBOLS_HPP__ + +#include +#include +#include +#include +#include "TracyCallstack.hpp" +#include "TracyDebug.hpp" +#include "TracyFastVector.hpp" +#include "TracyStringHelpers.hpp" +#include "../common/TracyAlloc.hpp" + +//TODO: move to DL_ITERATE_PHDR symbol file. +#if defined(TRACY_USE_LIBBACKTRACE) && TRACY_HAS_CALLSTACK != 4 // dl_iterate_phdr is required for the current image cache. Need to move it to libbacktrace? +# define TRACY_HAS_DL_ITERATE_PHDR_TO_REFRESH_IMAGE_CACHE +# include +#endif + +namespace tracy +{ +#ifdef TRACY_SYMBOL_OFFLINE_RESOLVE +constexpr bool s_shouldResolveSymbolsOffline = true; +#else +extern bool s_shouldResolveSymbolsOffline; +#endif // #ifdef TRACY_SYMBOL_OFFLINE_RESOLVE + +inline bool ShouldResolveSymbolsOffline() +{ +// when "TRACY_SYMBOL_OFFLINE_RESOLVE" is set, instead of fully resolving symbols at runtime, +// simply resolve the offset and image name (which will be enough the resolving to be done offline) + const char* symbolOfflineResolve = GetEnvVar( "TRACY_SYMBOL_OFFLINE_RESOLVE" ); + return (symbolOfflineResolve && symbolOfflineResolve[0] == '1'); +} + +static bool IsKernelAddress(uint64_t addr) { + return (addr >> 63) != 0; +} + +void DestroyImageEntry( ImageEntry& entry ) +{ + tracy_free( entry.m_path ); + tracy_free( entry.m_name ); +} + +class ImageCache +{ +public: + + ImageCache( size_t imageCacheCapacity = 512 ) + : m_images( imageCacheCapacity ) + { + } + + ~ImageCache() + { + Clear(); + } + + ImageEntry* AddEntry( const ImageEntry& entry ) + { + if( m_sorted ) m_sorted = m_images.empty() || ( entry.m_startAddress < m_images.back().m_startAddress ); + ImageEntry* newEntry = m_images.push_next(); + *newEntry = entry; + return newEntry; + } + + const ImageEntry* GetImageForAddress( uint64_t address ) + { + Sort(); + + auto it = std::lower_bound( m_images.begin(), m_images.end(), address, + []( const ImageEntry& lhs, const uint64_t rhs ) { return lhs.m_startAddress > rhs; } ); + + if( it != m_images.end() && address < it->m_endAddress ) + { + return it; + } + return nullptr; + } + + void Sort() + { + if( m_sorted ) return; + + std::sort( m_images.begin(), m_images.end(), + []( const ImageEntry& lhs, const ImageEntry& rhs ) { return lhs.m_startAddress > rhs.m_startAddress; } ); + m_sorted = true; + } + + void Clear() + { + for( ImageEntry& entry : m_images ) + { + DestroyImageEntry( entry ); + } + + m_sorted = true; + m_images.clear(); + } + + bool ContainsImage( uint64_t startAddress ) const + { + return std::any_of( m_images.begin(), m_images.end(), [startAddress]( const ImageEntry& entry ) { return startAddress == entry.m_startAddress; } ); + } +protected: + tracy::FastVector m_images; + bool m_sorted = true; +}; + +//TODO: move to DL_ITERATE_PHDR symbol file. +#ifdef TRACY_HAS_DL_ITERATE_PHDR_TO_REFRESH_IMAGE_CACHE +// when we have access to dl_iterate_phdr(), we can build a cache of address ranges to image paths +// so we can quickly determine which image an address falls into. +// We refresh this cache only when we hit an address that doesn't fall into any known range. +class ImageCacheDlIteratePhdr : public ImageCache +{ +public: + + ImageCacheDlIteratePhdr() + { + Refresh(); + } + + ~ImageCacheDlIteratePhdr() + { + } + + const ImageEntry* GetImageForAddress( uint64_t address ) + { + const ImageEntry* entry = ImageCache::GetImageForAddress( address ); + if( !entry ) + { + Refresh(); + return ImageCache::GetImageForAddress( address ); + } + return entry; + } + +private: + bool m_updated = false; + bool m_haveMainImageName = false; + + static int Callback( struct dl_phdr_info* info, size_t size, void* data ) + { + ImageCacheDlIteratePhdr* cache = reinterpret_cast( data ); + + const auto startAddress = static_cast( info->dlpi_addr ); + if( cache->ContainsImage( startAddress ) ) return 0; + + const uint32_t headerCount = info->dlpi_phnum; + assert( headerCount > 0); + const auto endAddress = static_cast( info->dlpi_addr + + info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr + info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz); + + ImageEntry image{}; + image.m_startAddress = startAddress; + image.m_endAddress = endAddress; + + // the base executable name isn't provided when iterating with dl_iterate_phdr, + // we will have to patch the executable image name outside this callback + image.m_name = info->dlpi_name && info->dlpi_name[0] != '\0' ? CopyStringFast( info->dlpi_name ) : nullptr; + + cache->AddEntry( image ); + cache->m_updated = true; + + return 0; + } + + void Refresh() + { + m_updated = false; + dl_iterate_phdr( Callback, this ); + + if( m_updated ) + { + Sort(); + // patch the main executable image name here, as calling dl_* functions inside the dl_iterate_phdr callback might cause deadlocks + UpdateMainImageName(); + } + } + + void UpdateMainImageName() + { + if( m_haveMainImageName ) + { + return; + } + + for( ImageEntry& entry : m_images ) + { + if( entry.m_name == nullptr ) + { + Dl_info dlInfo; + if( dladdr( (void *)entry.m_startAddress, &dlInfo ) ) + { + if( dlInfo.dli_fname ) + { + entry.m_name = CopyString( dlInfo.dli_fname ); + } + } + + // we only expect one entry to be null for the main executable entry + break; + } + } + + m_haveMainImageName = true; + } + void Clear() + { + ImageCache::Clear(); + m_haveMainImageName = false; + } +}; +using UserlandImageCache = ImageCacheDlIteratePhdr; +#else +using UserlandImageCache = ImageCache; +#endif //#ifdef TRACY_HAS_DL_ITERATE_PHDR_TO_REFRESH_IMAGE_CACHE + +extern UserlandImageCache* s_imageCache; +extern ImageCache* s_krnlCache; + +inline void CreateImageCaches() +{ + assert( s_imageCache == nullptr && s_krnlCache == nullptr ); + s_imageCache = new ( tracy_malloc( sizeof( UserlandImageCache ) ) ) UserlandImageCache(); + s_krnlCache = new ( tracy_malloc( sizeof( ImageCache ) ) ) ImageCache(); +} + +inline void DestroyImageCaches() +{ + if( s_krnlCache != nullptr ) + { + s_krnlCache->~ImageCache(); + tracy_free( s_krnlCache ); + s_krnlCache = nullptr; + } + + if( s_imageCache != nullptr ) + { + s_imageCache->~UserlandImageCache(); + tracy_free( s_imageCache ); + s_imageCache = nullptr; + } + +} + + +} + +#endif // __TRACYSYMBOLS_HPP__ \ No newline at end of file diff --git a/public/client/TracySymbolsDbgHelp.cpp b/public/client/TracySymbolsDbgHelp.cpp new file mode 100644 index 0000000000..b57569c9d3 --- /dev/null +++ b/public/client/TracySymbolsDbgHelp.cpp @@ -0,0 +1,543 @@ +#include +#include +#include +#include +#include "TracyDebug.hpp" +#include "TracyFastVector.hpp" +#include "TracyStringHelpers.hpp" +#include "../common/TracyAlloc.hpp" +#include "../common/TracySystem.hpp" +# include "TracySymbols.hpp" + +#ifndef NOMINMAX +# define NOMINMAX +#endif +#include +#include +#include +#ifdef _MSC_VER +# pragma warning( push ) +# pragma warning( disable : 4091 ) +#endif +#include +#pragma comment( lib, "dbghelp.lib" ) +#ifdef _MSC_VER +# pragma warning( pop ) +#endif + +#ifdef TRACY_DBGHELP_LOCK +# include "TracyProfiler.hpp" + +# define DBGHELP_INIT TracyConcat( TRACY_DBGHELP_LOCK, Init() ) +# define DBGHELP_LOCK TracyConcat( TRACY_DBGHELP_LOCK, Lock() ); +# define DBGHELP_UNLOCK TracyConcat( TRACY_DBGHELP_LOCK, Unlock() ); + +extern "C" +{ + void DBGHELP_INIT; + void DBGHELP_LOCK; + void DBGHELP_UNLOCK; +}; +#endif + +namespace tracy +{ + +constexpr size_t MaxCbTrace = 64; +constexpr size_t MaxNameSize = 8*1024; + +int cb_num; +CallstackEntry cb_data[MaxCbTrace]; + +extern "C" +{ + typedef DWORD (__stdcall *t_SymAddrIncludeInlineTrace)( HANDLE hProcess, DWORD64 Address ); + typedef BOOL (__stdcall *t_SymQueryInlineTrace)( HANDLE hProcess, DWORD64 StartAddress, DWORD StartContext, DWORD64 StartRetAddress, DWORD64 CurAddress, LPDWORD CurContext, LPDWORD CurFrameIndex ); + typedef BOOL (__stdcall *t_SymFromInlineContext)( HANDLE hProcess, DWORD64 Address, ULONG InlineContext, PDWORD64 Displacement, PSYMBOL_INFO Symbol ); + typedef BOOL (__stdcall *t_SymGetLineFromInlineContext)( HANDLE hProcess, DWORD64 qwAddr, ULONG InlineContext, DWORD64 qwModuleBaseAddress, PDWORD pdwDisplacement, PIMAGEHLP_LINE64 Line64 ); + + t_SymAddrIncludeInlineTrace _SymAddrIncludeInlineTrace = 0; + t_SymQueryInlineTrace _SymQueryInlineTrace = 0; + t_SymFromInlineContext _SymFromInlineContext = 0; + t_SymGetLineFromInlineContext _SymGetLineFromInlineContext = 0; +} + +static void SymError( const char* function, DWORD code ) { + char message[1024] = {}; + int written = snprintf( message, sizeof( message ), "ERROR: %s FAILED with code %u (0x%x) | ", function, code, code ); + written += FormatMessageA( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + code, + MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US), + (LPSTR)&message[written], + sizeof(message) - written, + NULL + ); + fprintf( stderr, "%s\n", message ); + OutputDebugStringA( message ); +} + +static void PrecacheProcessDriversAndModules(); + +void DbgHelpInit() +{ + if( ShouldResolveSymbolsOffline() ) return; + + _SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymAddrIncludeInlineTrace"); + _SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymQueryInlineTrace"); + _SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymFromInlineContext"); + _SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress(GetModuleHandleA("dbghelp.dll"), "SymGetLineFromInlineContext"); + +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_INIT; + DBGHELP_LOCK; +#endif + + // append executable path to the _NT_SYMBOL_PATH environment variable + char buffer [32767]; // max env var length on Windows (including null-terminator) + DWORD length = GetEnvironmentVariableA( "_NT_SYMBOL_PATH", buffer, sizeof( buffer ) ); + if( length > sizeof( buffer ) ) SymError( "GetEnvironmentVariableA", GetLastError() ); + else if( length + 1 >= sizeof( buffer ) ) SymError( "_TracyAppendEnvironmentVariable", ERROR_INSUFFICIENT_BUFFER ); + else + { + buffer[length] = ';'; + buffer[++length] = '\0'; + length += GetModuleFileNameA( NULL, &buffer[length], sizeof( buffer ) - length ); + if( length >= sizeof( buffer ) && GetLastError() == ERROR_INSUFFICIENT_BUFFER ) + { + SymError( "GetModuleFileNameA", GetLastError() ); + } + else + { + while( length > 0 && buffer[--length] != '\\' ) + buffer[length] = '\0'; + } + } + + assert( length < sizeof( buffer ) ); + if( SetEnvironmentVariableA( "_NT_SYMBOL_PATH", buffer ) == FALSE ) SymError( "SetEnvironmentVariableA", GetLastError() ); + + SymSetOptions( SymGetOptions() | SYMOPT_LOAD_LINES ); + if( SymInitialize( GetCurrentProcess(), NULL, TRUE ) == FALSE ) + { + SymError( "SymInitialize", GetLastError() ); + } + else if( GetModuleHandleA( "SymSrv.dll" ) == NULL ) + { + TracyDebug( "SymSrv.dll was not loaded, it needs to be near a matching version of DbgHelp.dll. Symbol resolution may fail as symbol servers will not be used. See https://learn.microsoft.com/en-us/windows/win32/debug/calling-the-dbghelp-library" ); + } + + PrecacheProcessDriversAndModules(); +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_UNLOCK; +#endif +} + +DWORD64 DbgHelpLoadSymbolsForModule( const char* imageName, uint64_t baseOfDll, uint32_t bllSize ) +{ + if( s_shouldResolveSymbolsOffline ) return 0; + return SymLoadModuleEx( GetCurrentProcess(), nullptr, imageName, nullptr, baseOfDll, bllSize, nullptr, 0 ); +} + +char* FormatImageName( const char* imageName, uint32_t imageNameLength ) +{ + // when doing offline symbol resolution, we must store the full path of the dll for the resolving to work + if( s_shouldResolveSymbolsOffline ) + { + return CopyStringFast( imageName, imageNameLength ); + } + else + { + const char* ptr = imageName + imageNameLength; + while( ptr > imageName && *ptr != '\\' && *ptr != '/' ) ptr--; + if( ptr > imageName ) ptr++; + const auto namelen = imageName + imageNameLength - ptr; + + char* alloc = (char*)tracy_malloc_fast( namelen + 3 ); + alloc[0] = '['; + memcpy( alloc + 1, ptr, namelen ); + alloc[namelen + 1] = ']'; + alloc[namelen + 2] = '\0'; + return alloc; + } +} + +ImageEntry* CacheModuleInfo( const char* imagePath, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) +{ + ImageEntry moduleEntry = {}; + moduleEntry.m_startAddress = baseOfDll; + moduleEntry.m_endAddress = baseOfDll + dllSize; + moduleEntry.m_path = CopyStringFast( imagePath, imageNameLength ); + moduleEntry.m_name = FormatImageName( imagePath, imageNameLength ); + + return s_imageCache->AddEntry( moduleEntry ); +} + +ImageEntry* LoadSymbolsForModuleAndCache( const char* imagePath, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) +{ + DbgHelpLoadSymbolsForModule( imagePath, baseOfDll, dllSize ); + return CacheModuleInfo( imagePath, imageNameLength, baseOfDll, dllSize ); +} + +static void CacheProcessDrivers() +{ + DWORD needed; + LPVOID dev[4096]; + if( EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 ) + { + char windir[MAX_PATH]; + if( !GetWindowsDirectoryA( windir, sizeof( windir ) ) ) memcpy( windir, "c:\\windows", 11 ); + const auto windirlen = strlen( windir ); + + const auto sz = needed / sizeof( LPVOID ); + for( size_t i=0; i", 2 ); + + ImageEntry kernelDriver{}; + kernelDriver.m_startAddress = (uint64_t)dev[i]; + kernelDriver.m_endAddress = 0; + kernelDriver.m_name = buf; + kernelDriver.m_path = nullptr; + + const auto len = GetDeviceDriverFileNameA( dev[i], fn, sizeof( fn ) ); + if( len != 0 ) + { + char full[MAX_PATH]; + char* path = fn; + + if( memcmp( fn, "\\SystemRoot\\", 12 ) == 0 ) + { + memcpy( full, windir, windirlen ); + strcpy( full + windirlen, fn + 11 ); + path = full; + } + + DbgHelpLoadSymbolsForModule( path, (DWORD64)dev[i], 0 ); + + kernelDriver.m_path = CopyString( path ); + } + + s_krnlCache->AddEntry(kernelDriver); + } + } + s_krnlCache->Sort(); + } +} + +static void CacheProcessModules() +{ + DWORD needed; + HANDLE proc = GetCurrentProcess(); + HMODULE mod[1024]; + if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 ) + { + const auto sz = needed / sizeof( HMODULE ); + for( size_t i=0; i 0 ) + { + // This may be a new module loaded since our call to SymInitialize. + // Just in case, force DbgHelp to load its pdb ! + LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); + } + } + } + } +} + +static void PrecacheProcessDriversAndModules() +{ + // use TRACY_NO_DBGHELP_INIT_LOAD=1 to disable preloading of driver + // and process module symbol loading at startup time - they will be loaded on demand later + // Sometimes this process can take a very long time and prevent resolving callstack frames + // symbols during that time. + const char* noInitLoadEnv = GetEnvVar("TRACY_NO_DBGHELP_INIT_LOAD"); + const bool initTimeModuleLoad = !(noInitLoadEnv && noInitLoadEnv[0] == '1'); + if (!initTimeModuleLoad) + { + TracyDebug("TRACY: skipping init time dbghelper module load"); + } + else + { + CacheProcessDrivers(); + CacheProcessModules(); + } +} + +const char* DecodeCallstackPtrFast( uint64_t ptr ) +{ + if( s_shouldResolveSymbolsOffline ) return "[unresolved]"; + + static char ret[MaxNameSize]; + const auto proc = GetCurrentProcess(); + + char buf[sizeof( SYMBOL_INFO ) + MaxNameSize]; + auto si = (SYMBOL_INFO*)buf; + si->SizeOfStruct = sizeof( SYMBOL_INFO ); + si->MaxNameLen = MaxNameSize; + +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_LOCK; +#endif + if( SymFromAddr( proc, ptr, nullptr, si ) == 0 ) + { + *ret = '\0'; + } + else + { + memcpy( ret, si->Name, si->NameLen ); + ret[si->NameLen] = '\0'; + } +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_UNLOCK; +#endif + return ret; +} + +const char* GetKernelModulePath( uint64_t addr ) +{ + assert( IsKernelAddress( addr ) ); + if( !s_krnlCache ) return nullptr; + const ImageEntry* imageEntry = s_krnlCache->GetImageForAddress( addr ); + if( imageEntry ) return imageEntry->m_path; + return nullptr; +} + +struct ModuleNameAndBaseAddress +{ + const char* name; + uint64_t baseAddr; +}; + +ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols( uint64_t addr ) +{ + if( IsKernelAddress( addr ) ) + { + const ImageEntry* entry = s_krnlCache->GetImageForAddress( addr ); + if( entry != nullptr ) return ModuleNameAndBaseAddress{ entry->m_name, entry->m_startAddress }; + return ModuleNameAndBaseAddress{ "", addr }; + } + + const ImageEntry* entry = s_imageCache->GetImageForAddress( addr ); + if( entry != nullptr ) return ModuleNameAndBaseAddress{ entry->m_name, entry->m_startAddress }; + + HANDLE proc = GetCurrentProcess(); + // Do not use FreeLibrary because we set the flag GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT + // see https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandleexa to get more information + constexpr DWORD flag = GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT; + HMODULE mod = NULL; + + InitRpmalloc(); + if( GetModuleHandleExA( flag, (char*)addr, &mod ) != 0 ) + { + MODULEINFO info; + if( GetModuleInformation( proc, mod, &info, sizeof( info ) ) != 0 ) + { + const auto base = uint64_t( info.lpBaseOfDll ); + if( addr >= base && addr < ( base + info.SizeOfImage ) ) + { + char name[1024]; + const auto nameLength = GetModuleFileNameA( mod, name, sizeof( name ) ); + if( nameLength > 0 ) + { + // since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize) + ImageEntry* cachedModule = LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); + return ModuleNameAndBaseAddress{ cachedModule->m_name, cachedModule->m_startAddress }; + } + } + } + } + + return ModuleNameAndBaseAddress{ "[unknown]", 0x0 }; +} + +CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) +{ + CallstackSymbolData sym; + + if( s_shouldResolveSymbolsOffline ) + { + sym.file = "[unknown]"; + sym.line = 0; + sym.needFree = false; + return sym; + } + + IMAGEHLP_LINE64 line; + DWORD displacement = 0; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_LOCK; +#endif + const auto res = SymGetLineFromAddr64( GetCurrentProcess(), ptr, &displacement, &line ); + if( res == 0 || line.LineNumber >= 0xF00000 ) + { + sym.file = "[unknown]"; + sym.line = 0; + sym.needFree = false; + } + else + { + sym.file = CopyString( line.FileName ); + sym.line = line.LineNumber; + sym.needFree = true; + } +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_UNLOCK; +#endif + return sym; +} + +static CallstackEntryData MakeUnresolvedCallstackEntryData( uint64_t ptr, ModuleNameAndBaseAddress moduleNameAndBaseAddress ) +{ + cb_data[0].symAddr = ptr - moduleNameAndBaseAddress.baseAddr; + cb_data[0].symLen = 0; + + cb_data[0].name = CopyStringFast( "[unresolved]" ); + cb_data[0].file = CopyStringFast( "[unknown]" ); + cb_data[0].line = 0; + + return { cb_data, 1, moduleNameAndBaseAddress.name }; +} + +CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) +{ +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_LOCK; +#endif + + InitRpmalloc(); + + const ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols( ptr ); + + if( s_shouldResolveSymbolsOffline ) + { +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_UNLOCK; +#endif + return MakeUnresolvedCallstackEntryData( ptr, moduleNameAndAddress ); + } + + int write; + const auto proc = GetCurrentProcess(); + +#if !defined TRACY_NO_CALLSTACK_INLINES + BOOL doInline = FALSE; + DWORD ctx = 0; + DWORD inlineNum = 0; + if( _SymAddrIncludeInlineTrace ) + { + inlineNum = _SymAddrIncludeInlineTrace( proc, ptr ); + if( inlineNum > MaxCbTrace - 1 ) inlineNum = MaxCbTrace - 1; + DWORD idx; + if( inlineNum != 0 ) doInline = _SymQueryInlineTrace( proc, ptr, 0, ptr, ptr, &ctx, &idx ); + } + if( doInline ) + { + write = inlineNum; + cb_num = 1 + inlineNum; + } + else +#endif + { + write = 0; + cb_num = 1; + } + + char buf[sizeof( SYMBOL_INFO ) + MaxNameSize]; + auto si = (SYMBOL_INFO*)buf; + si->SizeOfStruct = sizeof( SYMBOL_INFO ); + si->MaxNameLen = MaxNameSize; + + const auto symValid = SymFromAddr( proc, ptr, nullptr, si ) != 0; + + IMAGEHLP_LINE64 line; + DWORD displacement = 0; + line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + + { + const char* filename; + const auto res = SymGetLineFromAddr64( proc, ptr, &displacement, &line ); + if( res == 0 || line.LineNumber >= 0xF00000 ) + { + filename = "[unknown]"; + cb_data[write].line = 0; + } + else + { + filename = line.FileName; + cb_data[write].line = line.LineNumber; + } + + cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); + cb_data[write].file = CopyStringFast( filename ); + if( symValid ) + { + cb_data[write].symLen = si->Size; + cb_data[write].symAddr = si->Address; + } + else + { + cb_data[write].symLen = 0; + cb_data[write].symAddr = 0; + } + } + +#if !defined TRACY_NO_CALLSTACK_INLINES + if( doInline ) + { + for( DWORD i=0; iName, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); + cb.file = CopyStringFast( filename ); + if( symInlineValid ) + { + cb.symLen = si->Size; + cb.symAddr = si->Address; + } + else + { + cb.symLen = 0; + cb.symAddr = 0; + } + + ctx++; + } + } +#endif +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_UNLOCK; +#endif + + return { cb_data, uint8_t( cb_num ), moduleNameAndAddress.name }; +} + +} // namespace tracy \ No newline at end of file