From 38533543b15a59c0ae22151fe6bf92b8cdf67df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sat, 8 Mar 2025 18:13:32 +0100 Subject: [PATCH 01/40] Ignore example build files --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 3ba1bb16a0..61f18c39c8 100644 --- a/.gitignore +++ b/.gitignore @@ -34,3 +34,6 @@ compile_commands.json profiler/build/wasm/Tracy-release.* profiler/build/wasm/Tracy-debug.* profiler/build/wasm/embed.tracy +examples/ToyPathTracer/Windows/TestCpu +examples/ToyPathTracer/Windows/x64 +examples/ToyPathTracer/Windows/TestCpu.vcxproj.user From 8a9e92a912c4f64e30531f587bcad5c90f8b90c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sun, 9 Mar 2025 00:54:39 +0100 Subject: [PATCH 02/40] Fix D3D11 issues when TRACY_CALLSTACK==0 (default for examples) --- public/tracy/TracyD3D11.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/tracy/TracyD3D11.hpp b/public/tracy/TracyD3D11.hpp index acab383169..810aa225f9 100644 --- a/public/tracy/TracyD3D11.hpp +++ b/public/tracy/TracyD3D11.hpp @@ -431,7 +431,7 @@ using TracyD3D11Ctx = tracy::D3D11Ctx*; #define TracyD3D11SrcLocSymbol TracyConcat(__tracy_gpu_d3d11_source_location,TracyLine) #define TracyD3D11SrcLocObject(name, color) static constexpr tracy::SourceLocationData TracyD3D11SrcLocSymbol { name, TracyFunction, TracyFile, (uint32_t)TracyLine, color }; -#if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK +#if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK && TRACY_CALLSTACK > 0 # define TracyD3D11Zone( ctx, name ) TracyD3D11NamedZoneS( ctx, TracyD3D11UnnamedZone, name, TRACY_CALLSTACK, true ) # define TracyD3D11ZoneC( ctx, name, color ) TracyD3D11NamedZoneCS( ctx, TracyD3D11UnnamedZone, name, color, TRACY_CALLSTACK, true ) # define TracyD3D11NamedZone( ctx, varname, name, active ) TracyD3D11SrcLocObject(name, 0); tracy::D3D11ZoneScope varname( ctx, &TracyD3D11SrcLocSymbol, TRACY_CALLSTACK, active ); From 286ebd2051092757ff5148b7407aeeb502e143ca Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Thu, 20 Feb 2025 17:47:12 +0100 Subject: [PATCH 03/40] [Add] server callstack solver --- profiler/src/profiler/TracySourceView.cpp | 3 +- public/TracyClient.cpp | 1 + public/client/TracyCallstack.cpp | 997 ++++++++++++++---- public/client/TracyCallstack.hpp | 51 +- public/client/TracyFastVector.hpp | 3 + public/client/TracyProfiler.cpp | 135 ++- public/client/TracyProfiler.hpp | 8 + public/common/TracyDebugModulesHeaderFile.hpp | 37 + public/common/TracyProtocol.hpp | 1 + public/common/TracyQueue.hpp | 28 + public/tracy/Tracy.hpp | 4 +- server/TracyWorker.cpp | 465 +++++++- server/TracyWorker.hpp | 25 + 13 files changed, 1496 insertions(+), 262 deletions(-) create mode 100644 public/common/TracyDebugModulesHeaderFile.hpp diff --git a/profiler/src/profiler/TracySourceView.cpp b/profiler/src/profiler/TracySourceView.cpp index 780ef31818..10a9eb63ab 100644 --- a/profiler/src/profiler/TracySourceView.cpp +++ b/profiler/src/profiler/TracySourceView.cpp @@ -1157,7 +1157,8 @@ void SourceView::RenderSymbolView( Worker& worker, View& view ) const auto shortenName = view.GetShortenName(); auto sym = worker.GetSymbolData( m_symAddr ); - assert( sym ); + if (sym == nullptr) return; // Might no have received symbol info yet + ImGui::PushFont( m_bigFont ); ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) ); if( ButtonDisablable( " " ICON_FA_CARET_LEFT " ", m_historyCursor <= 1 ) ) diff --git a/public/TracyClient.cpp b/public/TracyClient.cpp index 6224f48bfe..a786bef362 100644 --- a/public/TracyClient.cpp +++ b/public/TracyClient.cpp @@ -31,6 +31,7 @@ #include "client/TracyAlloc.cpp" #include "client/TracyOverride.cpp" #include "client/TracyKCore.cpp" +#include "common/TracyModulesPdbSeacher.cpp" #if defined(TRACY_HAS_CALLSTACK) # if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 diff --git a/public/client/TracyCallstack.cpp b/public/client/TracyCallstack.cpp index 946a197212..a801791de8 100644 --- a/public/client/TracyCallstack.cpp +++ b/public/client/TracyCallstack.cpp @@ -1,17 +1,25 @@ #include #include #include -#include +#include // for memcpy +#include + #include "TracyCallstack.hpp" #include "TracyDebug.hpp" -#include "TracyFastVector.hpp" #include "TracyStringHelpers.hpp" #include "../common/TracyAlloc.hpp" #include "../common/TracySystem.hpp" +#include "../common/TracyDebugModulesHeaderFile.hpp" #ifdef TRACY_HAS_CALLSTACK +constexpr uint32_t ImageCacheAllocSize = 512; + +#define CLIENT_SEND_IMAGES_INFO "CLIENT_SEND_IMAGES_INFO" +#define SERVER_LOCAL_RESOLVE "SERVER_LOCAL_RESOLVE" + + #if TRACY_HAS_CALLSTACK == 1 # ifndef NOMINMAX # define NOMINMAX @@ -53,6 +61,144 @@ extern "C" }; #endif +//#define TRACY_USE_IMAGE_CACHE + +namespace tracy +{ + // 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; + + static bool s_clientSendImageInfo = false; + static bool s_serverLocalResolve = false; // Should the profiler try to resolve symbols before querying the client for symbols. + + inline bool IsEnv(const char* environementVariableName) + { + const char* v = GetEnvVar(environementVariableName); + + return (v && v[0] == '1'); + } + + struct ModuleNameAndBaseAddress + { + const char* name; + uint64_t baseAddr; + }; + + // The only threads that access the cache are the Symbol Worker and the Tracy Thread + // Only the symbol worker may write, but the Tracy Thread needs to be able to read the cache safely + // Since only the SymbolWorker is allowed to write the cache, it does not need to lock when reading. + static std::recursive_mutex s_cacheMutex; + std::recursive_mutex& GetModuleCacheMutexForRead() { + return s_cacheMutex; + } + + class ImageCache + { + public: + const ModuleCacheEntry* FindEntryFromAddr(uint64_t addr) const + { + for (size_t i = 0; i < m_modCache.size(); i++) + { + auto& it = m_modCache[i]; + + if (addr >= it.start && addr < it.end) + return ⁢ + + } + + auto it = std::lower_bound(m_modCache.begin(), m_modCache.end(), addr, [](const ModuleCacheEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); + if (it != m_modCache.end() && (addr < it->end)) + return &(*it); + + return nullptr; + } + + ModuleCacheEntry* PushBack() + { + return m_modCache.push_next(); + } + + + void MapModuleData(const ModuleCacheEntry** moduleCacheEntries, size_t* moduleCount) + { + if (!s_clientSendImageInfo && m_modCache.empty()) + { + *moduleCacheEntries = nullptr; + *moduleCount = 0; + return; + } + + *moduleCacheEntries = m_modCache.data(); + *moduleCount = m_modCache.size(); + } + + ModuleCacheEntry* CacheModuleWithDebugInfo(const ModuleCacheEntry& entry) + { + ModuleCacheEntry* it = PushBack(); + *it = entry; + return it; + } + + void Clear() + { + size_t mCacheS = 0; + + for (ModuleCacheEntry& cache : m_modCache) + { + if (cache.name != nullptr) + { + tracy_free(cache.name); + cache.name = nullptr; + } + + DegugModuleField& dbf = cache.degugModuleField; + + if (dbf.debugData != nullptr) + { + tracy_free(dbf.debugData); + dbf.debugData = nullptr; + } + mCacheS++; + } + m_modCache.clear(); + } + + bool Contain(const ModuleCacheEntry& moduleCacheEntry) + { + return std::find_if(m_modCache.begin(), m_modCache.end(), [moduleCacheEntry](const ModuleCacheEntry& module)->bool + { + return moduleCacheEntry.start == moduleCacheEntry.start; + }) != m_modCache.end(); + } + + const FastVector& GetModuleData() const + { + return m_modCache; + } + + ImageCache(size_t moduleCacheCapacity) : m_modCache(moduleCacheCapacity) + { + + } + + + ~ImageCache() + { + Clear(); + } + + + protected: + FastVector m_modCache; + + }; +} + + #if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 5 || TRACY_HAS_CALLSTACK == 6 // If you want to use your own demangling functionality (e.g. for another language), // define TRACY_DEMANGLE and provide your own implementation of the __tracy_demangle @@ -95,6 +241,7 @@ extern "C" const char* ___tracy_demangle( const char* mangled ) # define TRACY_USE_IMAGE_CACHE # include #endif +#include namespace tracy { @@ -103,46 +250,47 @@ namespace tracy // 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 ImageCache +class ImageCacheLinux : public ImageCache { public: - struct ImageEntry - { - void* m_startAddress = nullptr; - void* m_endAddress = nullptr; - char* m_name = nullptr; - }; - - ImageCache() - : m_images( 512 ) + ImageCacheLinux() + : ImageCache(ImageCacheAllocSize) { + // ?? Refresh(); } - ~ImageCache() + ~ImageCacheLinux() { - Clear(); + m_haveMainImageName = false; } - const ImageEntry* GetImageForAddress( void* address ) + const ModuleCacheEntry* GetImageForAddress( void* address ) { - const ImageEntry* entry = GetImageForAddressImpl( address ); - if( !entry ) + const ModuleCacheEntry* entry = FindEntryFromAddr( (uint64_t)address ); + + /*if (!entry) { Refresh(); return GetImageForAddressImpl( address ); - } + }*/ return entry; } private: - tracy::FastVector m_images; bool m_updated = false; bool m_haveMainImageName = false; + bool Contains(void* startAddress) const + { + uint64_t address = (uint64_t)startAddress; + return std::any_of(m_modCache.begin(), m_modCache.end(), [address](const ModuleCacheEntry& entry) { return address == entry.start; }); + } + static int Callback( struct dl_phdr_info* info, size_t size, void* data ) { - ImageCache* cache = reinterpret_cast( data ); + // SCARY + ImageCacheLinux* cache = reinterpret_cast( data ); const auto startAddress = reinterpret_cast( info->dlpi_addr ); if( cache->Contains( startAddress ) ) return 0; @@ -174,10 +322,7 @@ class ImageCache return 0; } - bool Contains( void* startAddress ) const - { - return std::any_of( m_images.begin(), m_images.end(), [startAddress]( const ImageEntry& entry ) { return startAddress == entry.m_startAddress; } ); - } + void Refresh() { @@ -186,8 +331,8 @@ class ImageCache if( m_updated ) { - std::sort( m_images.begin(), m_images.end(), - []( const ImageEntry& lhs, const ImageEntry& rhs ) { return lhs.m_startAddress > rhs.m_startAddress; } ); + std::sort( m_modCache.begin(), m_modCache.end(), + []( const ModuleCacheEntry& lhs, const ModuleCacheEntry& rhs ) { return lhs.start > rhs.start; } ); // patch the main executable image name here, as calling dl_* functions inside the dl_iterate_phdr callback might cause deadlocks UpdateMainImageName(); @@ -201,18 +346,18 @@ class ImageCache return; } - for( ImageEntry& entry : m_images ) + for(ModuleCacheEntry& entry : m_modCache) { - if( entry.m_name == nullptr ) + if( entry.name == nullptr ) { Dl_info dlInfo; - if( dladdr( (void *)entry.m_startAddress, &dlInfo ) ) + if( dladdr( (void *)entry.start, &dlInfo ) ) { if( dlInfo.dli_fname ) { size_t sz = strlen( dlInfo.dli_fname ) + 1; - entry.m_name = (char*)tracy_malloc( sz ); - memcpy( entry.m_name, dlInfo.dli_fname, sz ); + entry.name = (char*)tracy_malloc( sz ); + memcpy( entry.name, dlInfo.dli_fname, sz ); } } @@ -224,18 +369,6 @@ class ImageCache m_haveMainImageName = true; } - const ImageEntry* GetImageForAddressImpl( void* address ) const - { - auto it = std::lower_bound( m_images.begin(), m_images.end(), address, - []( const ImageEntry& lhs, const void* rhs ) { return lhs.m_startAddress > rhs; } ); - - if( it != m_images.end() && address < it->m_endAddress ) - { - return it; - } - return nullptr; - } - void Clear() { for( ImageEntry& entry : m_images ) @@ -249,16 +382,26 @@ class ImageCache }; #endif //#ifdef TRACY_USE_IMAGE_CACHE -// 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; + + + + +void FormatImageName(char** moduleCacheName, const char* imageName, uint32_t imageNameLength) +{ + auto ptr = imageName + imageNameLength; + while (ptr > imageName && *ptr != '\\' && *ptr != '/') ptr--; + if (ptr > imageName) ptr++; + const auto namelen = imageName + imageNameLength - ptr; + *moduleCacheName = (char*)tracy_malloc_fast(namelen + 3); + (*moduleCacheName)[0] = '['; + memcpy(*moduleCacheName + 1, ptr, namelen); + (*moduleCacheName)[namelen + 1] = ']'; + (*moduleCacheName)[namelen + 2] = '\0'; +} + bool ShouldResolveSymbolsOffline() { - const char* symbolOfflineResolve = GetEnvVar( "TRACY_SYMBOL_OFFLINE_RESOLVE" ); - return (symbolOfflineResolve && symbolOfflineResolve[0] == '1'); + return IsEnv("TRACY_SYMBOL_OFFLINE_RESOLVE"); } #endif // #ifdef TRACY_SYMBOL_OFFLINE_RESOLVE @@ -285,127 +428,311 @@ extern "C" TRACY_API ___tracy_t_RtlWalkFrameChain ___tracy_RtlWalkFrameChain = 0; } -struct ModuleCache +struct CV_INFO_PDB70 { - uint64_t start; - uint64_t end; - char* name; + DWORD CvSignature; + GUID Signature; + DWORD Age; + BYTE PdbFileName[1]; }; -static FastVector* s_modCache; +static constexpr auto mandatoryAlignment = 8; +static constexpr DWORD CV_SIGNATURE_RSDS = 'SDSR'; // 'SDSR' + + +FastVector* s_krnlCache = nullptr; -struct KernelDriver + + +class ImageCacheWindows : public ImageCache { - uint64_t addr; - const char* mod; - const char* path; +public: + + ImageCacheWindows(size_t moduleAllocationSize) : ImageCache(moduleAllocationSize) + { + + } + + ~ImageCacheWindows() = default; + +private: + }; -KernelDriver* s_krnlCache = nullptr; -size_t s_krnlCacheCnt; +ImageCacheWindows* s_imageCacheWindows = nullptr; -void InitCallstackCritical() + + +uint64_t DbgHelpLoadSymbolsForModule(const char* imageName, uint64_t baseOfDll, uint32_t dllSize) { - ___tracy_RtlWalkFrameChain = (___tracy_t_RtlWalkFrameChain)GetProcAddress( GetModuleHandleA( "ntdll.dll" ), "RtlWalkFrameChain" ); + auto d = SymLoadModuleEx(symHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0); + + IMAGEHLP_MODULEW64 moduleInfo{}; + moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); + if (TRUE == SymGetModuleInfoW64(symHandle, (uintptr_t)baseOfDll, &moduleInfo)) + { + wprintf(L"- ImageName=%s\n", moduleInfo.ImageName); + wprintf(L"- LoadedPdbName=%s\n", moduleInfo.LoadedPdbName); + } + + return d; } -void DbgHelpInit() + + +ModuleCacheEntry* LoadSymbolsForModuleAndCache(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) { - 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"); + DbgHelpLoadSymbolsForModule(imageName, baseOfDll, dllSize); + + std::lock_guard mutexguard{ s_cacheMutex }; -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_INIT; - DBGHELP_LOCK; -#endif + ModuleCacheEntry* cachedModuleEntry = s_imageCacheWindows->PushBack(); + cachedModuleEntry->start = baseOfDll; + cachedModuleEntry->end = baseOfDll + dllSize; - SymInitialize( GetCurrentProcess(), nullptr, true ); - SymSetOptions( SYMOPT_LOAD_LINES ); + // when doing offline symbol resolution, we must store the full path of the dll for the resolving to work + if (s_shouldResolveSymbolsOffline) + { + cachedModuleEntry->name = (char*)tracy_malloc_fast(imageNameLength + 1); + memcpy(cachedModuleEntry->name, imageName, imageNameLength); + cachedModuleEntry->name[imageNameLength] = '\0'; + } + else + { + FormatImageName(&cachedModuleEntry->name, imageName, imageNameLength); + } -#ifdef TRACY_DBGHELP_LOCK - DBGHELP_UNLOCK; -#endif + return cachedModuleEntry; } -DWORD64 DbgHelpLoadSymbolsForModule( const char* imageName, uint64_t baseOfDll, uint32_t bllSize ) +void CacheKernelModule(const char* imageName, uint64_t baseOfDll, uint32_t dllSize, DebugFormat* debugFormat , uint8_t** debugData, uint32_t* debugSize) { - if( s_shouldResolveSymbolsOffline ) return 0; - return SymLoadModuleEx( GetCurrentProcess(), nullptr, imageName, nullptr, baseOfDll, bllSize, nullptr, 0 ); + auto d = SymLoadModuleEx(symHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0); + + IMAGEHLP_MODULEW64 moduleInfo{}; + moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); + if (TRUE == SymGetModuleInfoW64(symHandle, (uintptr_t)baseOfDll, &moduleInfo)) + { + *debugFormat = DebugFormat::PdbDebugFormat; + + static constexpr int endofString = 1; + const uint32_t signatureLenght = static_cast(wcslen(reinterpret_cast(moduleInfo.CVData))); + + const uint32_t debugFormatSize = sizeof(WindowsDebugData) + signatureLenght + endofString; + + *debugData = (uint8_t*)tracy_malloc(debugFormatSize); + *debugSize = debugFormatSize; + WindowsDebugData* ptrToWindowsDebugData = reinterpret_cast(*debugData); + ptrToWindowsDebugData->majorVersion = 0; + ptrToWindowsDebugData->minorVersion = 0; + ptrToWindowsDebugData->exeDataTimeStamp = moduleInfo.TimeDateStamp; + + TracyPdbInfo* pdbInfo = &ptrToWindowsDebugData->cvInfo; + + pdbInfo->Age = moduleInfo.PdbAge; + pdbInfo->CvSignature = moduleInfo.CVSig; + + static_assert(sizeof(pdbInfo->Signature) == sizeof(moduleInfo.PdbSig70), "GUID size must match"); + memcpy(&pdbInfo->Signature, &moduleInfo.PdbSig70, sizeof(moduleInfo.PdbSig70)); + + const GUID* guidd = reinterpret_cast(&pdbInfo->Signature); + + using convert_type = std::codecvt_utf8; + + std::wstring s = moduleInfo.CVData; + std::wstring_convert converter; + std::string converted_str = converter.to_bytes(s); + + char* outSingature = (char*)memcpy((*debugData) + sizeof(WindowsDebugData) + , converted_str.data(), signatureLenght + endofString); + + const CV_INFO_PDB70* pData = reinterpret_cast(pdbInfo); + + SymUnloadModule(symHandle, baseOfDll); + } + } -ModuleCache* LoadSymbolsForModuleAndCache( const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) + +bool GetModulePDBData(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize) { - DbgHelpLoadSymbolsForModule( imageName, baseOfDll, dllSize ); + if (!s_clientSendImageInfo) + return false; - ModuleCache* cachedModule = s_modCache->push_next(); - cachedModule->start = baseOfDll; - cachedModule->end = baseOfDll + dllSize; + static constexpr bool MappedAsImage = true; - // when doing offline symbol resolution, we must store the full path of the dll for the resolving to work - if( s_shouldResolveSymbolsOffline ) + PVOID BaseAddress = (void*)baseOfDll; + + PIMAGE_NT_HEADERS header = ImageNtHeader(BaseAddress); + + ULONG debugDirectoryCount = 0; + IMAGE_SECTION_HEADER* debugSectionHeader; + + PVOID debugSectionData = ImageDirectoryEntryToDataEx(BaseAddress, true, IMAGE_DIRECTORY_ENTRY_DEBUG, + &debugDirectoryCount, &debugSectionHeader); + + if (debugSectionData == NULL) { - cachedModule->name = (char*)tracy_malloc_fast(imageNameLength + 1); - memcpy(cachedModule->name, imageName, imageNameLength); - cachedModule->name[imageNameLength] = '\0'; + return false; } - else + IMAGE_DEBUG_DIRECTORY* debugDirectory = static_cast(debugSectionData); + + for (size_t i = 0; (i * sizeof(IMAGE_DEBUG_DIRECTORY)) < debugDirectoryCount; i++) { - auto ptr = imageName + imageNameLength; - while (ptr > imageName && *ptr != '\\' && *ptr != '/') ptr--; - if (ptr > imageName) ptr++; - const auto namelen = imageName + imageNameLength - ptr; - cachedModule->name = (char*)tracy_malloc_fast(namelen + 3); - cachedModule->name[0] = '['; - memcpy(cachedModule->name + 1, ptr, namelen); - cachedModule->name[namelen + 1] = ']'; - cachedModule->name[namelen + 2] = '\0'; + + const IMAGE_DEBUG_DIRECTORY& curDebugDirectory = debugDirectory[i]; + + if (debugDirectory[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW) continue; + + CV_INFO_PDB70* pData = + (CV_INFO_PDB70*)(uintptr_t(BaseAddress) + (MappedAsImage ? debugDirectory[i].AddressOfRawData + : debugDirectory[i].PointerToRawData)); + + if (pData->CvSignature != CV_SIGNATURE_RSDS) continue; + + *debugFormat = DebugFormat::PdbDebugFormat; + + const uint32_t pdbFileLength = strlen((const char*)pData->PdbFileName); + const uint32_t debugFormatSize = sizeof(WindowsDebugData) + pdbFileLength + 1; + *debugInformationData = (uint8_t*)tracy_malloc(debugFormatSize); + *debugInformationSize = debugFormatSize; + + uint8_t* ptrToDebugPacket = reinterpret_cast(*debugInformationData); + WindowsDebugData* ptrToWindowsDebugData = reinterpret_cast(ptrToDebugPacket); + + // write minor major version + ptrToWindowsDebugData->majorVersion = curDebugDirectory.MajorVersion; + ptrToWindowsDebugData->minorVersion = curDebugDirectory.MinorVersion; + + ptrToWindowsDebugData->exeDataTimeStamp = curDebugDirectory.TimeDateStamp; + ptrToWindowsDebugData->cvInfo.Age = pData->Age; + ptrToWindowsDebugData->cvInfo.CvSignature = pData->CvSignature; + static_assert(sizeof(GUID) == sizeof(pData->Signature), "GUID size must match"); + memcpy(&ptrToWindowsDebugData->cvInfo.Signature, &pData->Signature, sizeof(pData->Signature)); + + memcpy(ptrToDebugPacket + sizeof(WindowsDebugData), pData->PdbFileName, pdbFileLength + 1); + + return true; } + return false; +} - return cachedModule; +ModuleNameAndBaseAddress OnFailedFindAddress(uint64_t address) +{ + InitRpmalloc(); + HMODULE mod[1024]; + DWORD needed; + HANDLE proc = GetCurrentProcess(); + + if (EnumProcessModules(proc, mod, sizeof(mod), &needed) != 0) + { + const auto sz = needed / sizeof(HMODULE); + for (size_t i = 0; i < sz; i++) + { + MODULEINFO info; + if (GetModuleInformation(proc, mod[i], &info, sizeof(info)) != 0) + { + const auto base = uint64_t(info.lpBaseOfDll); + if (address >= base && address < base + info.SizeOfImage) + { + char name[1024]; + const auto nameLength = GetModuleFileNameA(mod[i], name, 1024); + if (nameLength > 0) + { + // since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize) + ModuleCacheEntry* moduleCacheEntry = LoadSymbolsForModuleAndCache(name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage); + return ModuleNameAndBaseAddress{ moduleCacheEntry->name, moduleCacheEntry->start }; + } + } + } + } + } + return ModuleNameAndBaseAddress{ "[unknown]", address }; } -void InitCallstack() +ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols(uint64_t addr, bool* failed) { -#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE - s_shouldResolveSymbolsOffline = ShouldResolveSymbolsOffline(); -#endif //#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE - if( s_shouldResolveSymbolsOffline ) + if ((addr >> 63) != 0) { - TracyDebug("TRACY: enabling offline symbol resolving!\n"); + if (s_krnlCache) + { + auto it = std::lower_bound(s_krnlCache->begin(), s_krnlCache->end(), addr, [](const ModuleCacheEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); + if (it != s_krnlCache->end()) + { + return ModuleNameAndBaseAddress{ it->name, it->start }; + } + } + return ModuleNameAndBaseAddress{ "", addr }; } - DbgHelpInit(); + const ModuleCacheEntry* entry = s_imageCacheWindows->FindEntryFromAddr(addr); + + if (entry != nullptr) + return ModuleNameAndBaseAddress{ entry->name, entry->start }; + + + if (s_serverLocalResolve) + { + *failed = true; + return ModuleNameAndBaseAddress{ "[unknown]", addr }; + } + + return OnFailedFindAddress(addr); +} + + + + +void InitCallstackCritical() +{ + ___tracy_RtlWalkFrameChain = (___tracy_t_RtlWalkFrameChain)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlWalkFrameChain"); +} + +void DbgHelpInit(bool invadeProcess) +{ + _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 - // 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\n"); - } + assert(SymInitialize(symHandle, nullptr, false), "Failed to Init SymInitialize"); + + //if (s_serverLocalResolve) + // SymSetSearchPath(symHandle, R"(srv*C:\Users\Gabriel\AppData\Local\Temp\SymbolCache*https://msdl.microsoft.com/download/symbols)"); + + SymSetOptions(SYMOPT_LOAD_LINES + | SYMOPT_UNDNAME // TODO: check if tracy doesn't rely on this to find decorated names in the dissassembler + /* | SYMOPT_DEFERRED_LOADS*/ +#ifndef NDEBUG + | SYMOPT_DEBUG +#endif + ); + + +#ifdef TRACY_DBGHELP_LOCK + DBGHELP_UNLOCK; +#endif +} + +void CacheProcessDrivers() +{ DWORD needed; LPVOID dev[4096]; - if( initTimeModuleLoad && EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 ) + 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 ); - s_krnlCache = (KernelDriver*)tracy_malloc( sizeof(KernelDriver) * sz ); + const auto sz = needed / sizeof(LPVOID); int cnt = 0; for( size_t i=0; i", 2 ); - s_krnlCache[cnt] = KernelDriver { (uint64_t)dev[i], buf }; + memcpy(buf + 1, fn, len); + memcpy(buf + len + 1, ">", 2); + ModuleCacheEntry* kernelDriver = s_krnlCache->push_next(); + + kernelDriver->start = (uint64_t)dev[i]; + kernelDriver->end = 0; + kernelDriver->name = buf; + kernelDriver->path = nullptr; + kernelDriver->degugModuleField = {}; const auto len = GetDeviceDriverFileNameA( dev[i], fn, sizeof( fn ) ); if( len != 0 ) @@ -432,59 +765,208 @@ void InitCallstack() path = full; } - DbgHelpLoadSymbolsForModule( path, (DWORD64)dev[i], 0 ); + if (!s_clientSendImageInfo) + DbgHelpLoadSymbolsForModule(path, (DWORD64)dev[i], 0); + else + CacheKernelModule(path, kernelDriver->start, 0, &kernelDriver->degugModuleField.debugFormat, &kernelDriver->degugModuleField.debugData, + &kernelDriver->degugModuleField.debugDataSize); const auto psz = strlen( path ); auto pptr = (char*)tracy_malloc_fast( psz+1 ); memcpy( pptr, path, psz ); pptr[psz] = '\0'; - s_krnlCache[cnt].path = pptr; + kernelDriver->path = pptr; } + assert(kernelDriver->end == 0 && "kernel end should be zero"); cnt++; } } - s_krnlCacheCnt = cnt; - std::sort( s_krnlCache, s_krnlCache + s_krnlCacheCnt, []( const KernelDriver& lhs, const KernelDriver& rhs ) { return lhs.addr > rhs.addr; } ); + std::sort(s_krnlCache->begin(), s_krnlCache->end(), [](const ModuleCacheEntry& lhs, const ModuleCacheEntry& rhs) { return lhs.start > rhs.start; }); } +} - s_modCache = (FastVector*)tracy_malloc( sizeof( FastVector ) ); - new(s_modCache) FastVector( 512 ); - +void CacheProcessModule() +{ + DWORD needed; HANDLE proc = GetCurrentProcess(); HMODULE mod[1024]; - if( initTimeModuleLoad && EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 ) + if (EnumProcessModules(proc, mod, sizeof(mod), &needed) != 0) { - const auto sz = needed / sizeof( HMODULE ); - for( size_t i=0; i 0 ) + const auto nameLength = GetModuleFileNameA(mod[i], name, 1021); + if (nameLength > 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 ); + if (!s_shouldResolveSymbolsOffline) + { + auto moduleCache = LoadSymbolsForModuleAndCache(name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage); + + } + else + { + uint64_t baseAdd = (DWORD64)info.lpBaseOfDll; + + ModuleCacheEntry* cachedModule = s_imageCacheWindows->PushBack(); + cachedModule->start = (DWORD64)info.lpBaseOfDll; + cachedModule->end = baseAdd + info.SizeOfImage; + cachedModule->path = (char*)tracy_malloc(nameLength); + memcpy(cachedModule->path, name, nameLength); + + FormatImageName(&cachedModule->name, name, nameLength); + + DebugFormat debugFormat = DebugFormat::NoDebugFormat; + uint8_t* debugData = nullptr; + uint32_t debugDataSize = 0; + + if (GetModulePDBData(cachedModule->start, &debugFormat, &debugData, &debugDataSize)) + { + DegugModuleField& dmf = cachedModule->degugModuleField; + + dmf.debugFormat = debugFormat; + dmf.debugData = debugData; + dmf.debugDataSize = debugDataSize; + } + + } + } } } } +} + + +void CacheDriverAndModuleData() +{ + symHandle = GetCurrentProcess(); + DbgHelpInit(true); + + // 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\n"); + } + + std::lock_guard mutexguard{ s_cacheMutex }; + + s_imageCacheWindows = (ImageCacheWindows*)tracy_malloc(sizeof(ImageCacheWindows)); + new(s_imageCacheWindows) ImageCacheWindows(ImageCacheAllocSize); + + s_krnlCache = (FastVector*)tracy_malloc(sizeof(FastVector)); + new(s_krnlCache) FastVector(ImageCacheAllocSize); + + + if (initTimeModuleLoad) + { + CacheProcessDrivers(); + CacheProcessModule(); + } +} + + +void InitCallstack() +{ + s_serverLocalResolve = IsEnv(SERVER_LOCAL_RESOLVE); + s_clientSendImageInfo = IsEnv(CLIENT_SEND_IMAGES_INFO); + + +#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE + s_shouldResolveSymbolsOffline = ShouldResolveSymbolsOffline(); +#endif //#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE + if (s_shouldResolveSymbolsOffline) + { + TracyDebug("TRACY: enabling offline symbol resolving!\n"); + } + + if (!s_shouldResolveSymbolsOffline && !s_clientSendImageInfo && !s_serverLocalResolve) + { + CacheDriverAndModuleData(); + return; + } + + + // the client will send all of his moduleData + if (s_clientSendImageInfo) + { + CacheDriverAndModuleData(); + } + + if (s_serverLocalResolve) + { + // Init debg and allocate moduleCache + // waiting for client sending his module datas + symHandle = (HANDLE)42; + static constexpr bool shoulInvadeProcess = false; + + DbgHelpInit(shoulInvadeProcess); + s_imageCacheWindows = (ImageCacheWindows*)tracy_malloc(sizeof(ImageCacheWindows)); + new(s_imageCacheWindows) ImageCacheWindows(ImageCacheAllocSize); + s_krnlCache = (FastVector*)tracy_malloc(sizeof(FastVector)); + new(s_krnlCache) FastVector(ImageCacheAllocSize); + } + // TO DO FIX IT MAY BE BROKEN #ifdef TRACY_DBGHELP_LOCK DBGHELP_UNLOCK; #endif } +void FreeKernelCache() +{ + if (s_krnlCache == nullptr) + { + return; + } + + for (auto& it : *s_krnlCache) + { + + if (it.name != nullptr) + { + tracy_free((void*)it.name); + it.name = nullptr; + } + + if (it.path != nullptr) + { + tracy_free((void*)it.path); + it.path = nullptr; + } + + } + tracy_free((void*)s_krnlCache); + s_krnlCache = nullptr; + +} + void EndCallstack() { + FreeKernelCache(); + + if (s_imageCacheWindows != nullptr) + { + s_imageCacheWindows->~ImageCacheWindows(); + tracy_free(s_imageCacheWindows); + s_imageCacheWindows = nullptr; + } } -const char* DecodeCallstackPtrFast( uint64_t ptr ) +const char* DecodeCallstackPtrFast(uint64_t ptr) { - if( s_shouldResolveSymbolsOffline ) return "[unresolved]"; + if (s_shouldResolveSymbolsOffline) return "[unresolved]"; static char ret[MaxNameSize]; const auto proc = GetCurrentProcess(); @@ -512,81 +994,143 @@ const char* DecodeCallstackPtrFast( uint64_t ptr ) return ret; } -const char* GetKernelModulePath( uint64_t addr ) +const char* GetKernelModulePath(uint64_t addr) { - assert( addr >> 63 != 0 ); - if( !s_krnlCache ) return nullptr; - auto it = std::lower_bound( s_krnlCache, s_krnlCache + s_krnlCacheCnt, addr, []( const KernelDriver& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } ); - if( it == s_krnlCache + s_krnlCacheCnt ) return nullptr; + assert(addr >> 63 != 0); + if (!s_krnlCache) return nullptr; + auto it = std::lower_bound(s_krnlCache->begin(), s_krnlCache->end(), addr, [](const ModuleCacheEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); + if (it == s_krnlCache->end()) return nullptr; return it->path; -} -struct ModuleNameAndBaseAddress +} +bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, DebugFormat debugFormat, const uint8_t* debugData, uint32_t debugDataSize) { - const char* name; - uint64_t baseAddr; -}; + assert(debugFormat == DebugFormat::PdbDebugFormat); -ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols( uint64_t addr ) -{ - if( ( addr >> 63 ) != 0 ) + const uint32_t DataForDebugSize = static_cast(debugDataSize); + assert(moduleName != nullptr); + + const tracy::WindowsDebugData* windowsDebugData = (const tracy::WindowsDebugData*)(debugData); + const size_t pdbPathLength = debugDataSize - sizeof(tracy::WindowsDebugData); + const uint8_t* pdbPath = debugData + sizeof(tracy::WindowsDebugData); + + static_assert(sizeof(WindowsDebugData) == 32, "Structure changed or not properly packed"); + + static_assert(offsetof(CV_INFO_PDB70, CvSignature) == offsetof(TracyPdbInfo, CvSignature), "Mismatch with DbgHelp headers."); + static_assert(offsetof(CV_INFO_PDB70, Signature) == offsetof(TracyPdbInfo, Signature), "Mismatch with DbgHelp headers."); + static_assert(offsetof(CV_INFO_PDB70, Age) == offsetof(TracyPdbInfo, Age), "Mismatch with DbgHelp headers."); + static_assert(offsetof(CV_INFO_PDB70, PdbFileName) == sizeof(TracyPdbInfo), "Mismatch with DbgHelp headers."); + + const uint32_t sizeOfPdbData = DataForDebugSize - offsetof(WindowsDebugData, cvInfo); + + auto const debug_module_info_size = sizeof(IMAGE_DEBUG_DIRECTORY) + sizeOfPdbData; + auto const debug_module_info_size_aligned = (debug_module_info_size + mandatoryAlignment) & (~uint64_t(mandatoryAlignment - 1)); + + std::vector dataAligned; + dataAligned.resize(debug_module_info_size_aligned); + + IMAGE_DEBUG_DIRECTORY* info = reinterpret_cast(dataAligned.data()); + + info->TimeDateStamp = windowsDebugData->exeDataTimeStamp; + info->Characteristics = 0; + info->MajorVersion = windowsDebugData->majorVersion; + info->MinorVersion = windowsDebugData->minorVersion; + info->Type = IMAGE_DEBUG_TYPE_CODEVIEW; + info->AddressOfRawData = 0; + info->PointerToRawData = sizeof(IMAGE_DEBUG_DIRECTORY); + info->SizeOfData = sizeOfPdbData; + + memcpy(dataAligned.data() + info->PointerToRawData, &windowsDebugData->cvInfo, sizeOfPdbData); + + MODLOAD_DATA module_load_info; + module_load_info.ssize = sizeof(module_load_info); + module_load_info.ssig = DBHHEADER_DEBUGDIRS; + module_load_info.data = dataAligned.data(); + module_load_info.size = static_cast(dataAligned.size()); + module_load_info.flags = 0; + + const CV_INFO_PDB70* dummie = reinterpret_cast(&windowsDebugData->cvInfo); + + + DWORD64 loaddedModule = SymLoadModuleEx(symHandle, NULL, moduleName, NULL, baseAddress, + dllSize, &module_load_info, 0); + + IMAGEHLP_MODULEW64 modulInfoDebug{}; + + modulInfoDebug.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); + + if (SymGetModuleInfoW64(symHandle, loaddedModule, &modulInfoDebug) == TRUE) { - if( s_krnlCache ) + if (modulInfoDebug.SymType != SymNone) { - auto it = std::lower_bound( s_krnlCache, s_krnlCache + s_krnlCacheCnt, addr, []( const KernelDriver& lhs, const uint64_t& rhs ) { return lhs.addr > rhs; } ); - if( it != s_krnlCache + s_krnlCacheCnt ) - { - return ModuleNameAndBaseAddress{ it->mod, it->addr }; - } + return true; } - return ModuleNameAndBaseAddress{ "", addr }; } + return false; +} - for( auto& v : *s_modCache ) +bool IsKernelAddress(uint64_t addr) { + return (addr >> 63) != 0; +} + +// Called from the profiler only, we received data from the client. +void CacheModuleAndLoadExternal(const ModuleCacheEntry& moduleCacheEntry) +{ + if (!s_serverLocalResolve) + return; + +#if 1 // windows + if (IsKernelAddress(moduleCacheEntry.start)) { - if( addr >= v.start && addr < v.end ) - { - return ModuleNameAndBaseAddress{ v.name, v.start }; - } + ModuleCacheEntry& newKernel = *s_krnlCache->push_next(); + newKernel = moduleCacheEntry; + } + else + { + s_imageCacheWindows->CacheModuleWithDebugInfo(moduleCacheEntry); } +#endif - HMODULE mod[1024]; - DWORD needed; - HANDLE proc = GetCurrentProcess(); + bool hasSymbolInfo = false; + if (moduleCacheEntry.degugModuleField.debugFormat == DebugFormat::PdbDebugFormat) + { + hasSymbolInfo = LoadFromPdb(moduleCacheEntry.name, moduleCacheEntry.start, moduleCacheEntry.end - moduleCacheEntry.start, + moduleCacheEntry.degugModuleField.debugFormat, moduleCacheEntry.degugModuleField.debugData, moduleCacheEntry.degugModuleField.debugDataSize); + } - InitRpmalloc(); - if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 ) + if (!hasSymbolInfo) { - const auto sz = needed / sizeof( HMODULE ); - for( size_t i=0; i= base && addr < base + info.SizeOfImage ) - { - char name[1024]; - const auto nameLength = GetModuleFileNameA( mod[i], name, 1021 ); - if( nameLength > 0 ) - { - // since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize) - ModuleCache* cachedModule = LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); - return ModuleNameAndBaseAddress{ cachedModule->name, cachedModule->start }; - } - } - } - } + // TODO: load from path only if we can check we got the correct binary (check timestamp / guid ?) + //DbgHelpLoadSymbolsForModule(moduleCacheEntry.path, moduleCacheEntry.start, moduleCacheEntry.end - moduleCacheEntry.start); + } + +} + +void CacheModuleKernelAndLoadExternal(const ModuleCacheEntry& kernelDriver) +{ + if(!s_serverLocalResolve) + return; + + + bool hasSymbolInfo = false; + if (kernelDriver.degugModuleField.debugFormat == DebugFormat::PdbDebugFormat) + { + hasSymbolInfo = LoadFromPdb(kernelDriver.path, kernelDriver.start, 0, kernelDriver.degugModuleField.debugFormat, kernelDriver.degugModuleField.debugData, kernelDriver.degugModuleField.debugDataSize); + } + + if (!hasSymbolInfo) + { + // TODO: load from path only if we can check we got the correct binary (check timestamp / guid ?) + //DbgHelpLoadSymbolsForModule(kernelDriver.path, kernelDriver.start, 0); } - return ModuleNameAndBaseAddress{ "[unknown]", 0x0 }; } -CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) +CallstackSymbolData DecodeSymbolAddress(uint64_t ptr) { CallstackSymbolData sym; - if( s_shouldResolveSymbolsOffline ) + if (s_shouldResolveSymbolsOffline) { sym.file = "[unknown]"; sym.line = 0; @@ -600,8 +1144,8 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) #ifdef TRACY_DBGHELP_LOCK DBGHELP_LOCK; #endif - const auto res = SymGetLineFromAddr64( GetCurrentProcess(), ptr, &displacement, &line ); - if( res == 0 || line.LineNumber >= 0xF00000 ) + const auto res = SymGetLineFromAddr64(symHandle, ptr, &displacement, &line); + if (res == 0 || line.LineNumber >= 0xF00000) { sym.file = "[unknown]"; sym.line = 0; @@ -619,7 +1163,17 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) return sym; } -CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) +const FastVector& GetModuleData() +{ + return s_imageCacheWindows->GetModuleData(); +} + +const FastVector& GetKernelDriver() +{ + return *s_krnlCache; +} + +CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus) { #ifdef TRACY_DBGHELP_LOCK DBGHELP_LOCK; @@ -627,14 +1181,21 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) InitRpmalloc(); - const ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols( ptr ); + bool reseachFailed = false; + const ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols(ptr, &reseachFailed); - if( s_shouldResolveSymbolsOffline ) + + if (reseachFailed) + { + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::ModuleMissing; + } + + if (s_shouldResolveSymbolsOffline || reseachFailed) { #ifdef TRACY_DBGHELP_LOCK DBGHELP_UNLOCK; #endif - + // may use symLen for base adress cb_data[0].symAddr = ptr - moduleNameAndAddress.baseAddr; cb_data[0].symLen = 0; @@ -646,7 +1207,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) } int write; - const auto proc = GetCurrentProcess(); + const auto proc = symHandle; #if !defined TRACY_NO_CALLSTACK_INLINES BOOL doInline = FALSE; @@ -671,12 +1232,19 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) cb_num = 1; } - char buf[sizeof( SYMBOL_INFO ) + MaxNameSize]; + char buf[sizeof(SYMBOL_INFO) + MaxNameSize]; auto si = (SYMBOL_INFO*)buf; - si->SizeOfStruct = sizeof( SYMBOL_INFO ); + si->SizeOfStruct = sizeof(SYMBOL_INFO); si->MaxNameLen = MaxNameSize; - const auto symValid = SymFromAddr( proc, ptr, nullptr, si ) != 0; + + const auto symValid = SymFromAddr(proc, ptr, nullptr, si) != 0; + + if (!symValid) + { + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::SymbolMissing; + } + IMAGEHLP_LINE64 line; DWORD displacement = 0; @@ -750,9 +1318,26 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) DBGHELP_UNLOCK; #endif - return { cb_data, uint8_t( cb_num ), moduleNameAndAddress.name }; + + return { cb_data, uint8_t(cb_num), moduleNameAndAddress.name }; +} + +void FindModuleFromAddr(uint64_t addr, const ModuleCacheEntry** entry) +{ + *entry = s_imageCacheWindows->FindEntryFromAddr(addr); +} + +void FindKernelDriverFromAddr(uint64_t addr, const ModuleCacheEntry** outDrive) +{ + auto it = std::lower_bound(s_krnlCache->begin(), s_krnlCache->end(), addr, [](const ModuleCacheEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); + if (it != s_krnlCache->end()) + { + *outDrive = it; + } } + + #elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 enum { MaxCbTrace = 64 }; diff --git a/public/client/TracyCallstack.hpp b/public/client/TracyCallstack.hpp index 12bea967f5..69d59328da 100644 --- a/public/client/TracyCallstack.hpp +++ b/public/client/TracyCallstack.hpp @@ -33,14 +33,27 @@ static tracy_force_inline void* Callstack( int32_t /*depth*/ ) { return nullptr; #include #include +#include #include "../common/TracyAlloc.hpp" +#include "TracyFastVector.hpp" namespace tracy { static constexpr bool has_callstack() { return true; } +enum struct DecodeCallStackPtrStatus +{ + Success, + ModuleMissing, + SymbolMissing, + + Count +}; + +enum struct DebugFormat : uint8_t; + struct CallstackSymbolData { const char* file; @@ -55,7 +68,7 @@ struct CallstackEntry const char* file; uint32_t line; uint32_t symLen; - uint64_t symAddr; + uint64_t symAddr; // Relative address }; struct CallstackEntryData @@ -65,13 +78,47 @@ struct CallstackEntryData const char* imageName; }; +struct DegugModuleField +{ + DebugFormat debugFormat; + uint8_t* debugData; + uint32_t debugDataSize; +}; + +struct ModuleCacheEntry +{ + uint64_t start; + uint64_t end; + char* name; + char* path; + + DegugModuleField degugModuleField; +}; + + +std::recursive_mutex& GetModuleCacheMutexForRead(); + CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ); const char* DecodeCallstackPtrFast( uint64_t ptr ); -CallstackEntryData DecodeCallstackPtr( uint64_t ptr ); +CallstackEntryData DecodeCallstackPtr( uint64_t ptr , DecodeCallStackPtrStatus* _decodeCallStackPtrStatus); + + void InitCallstack(); void InitCallstackCritical(); void EndCallstack(); const char* GetKernelModulePath( uint64_t addr ); +void FindModuleFromAddr(uint64_t addr, const ModuleCacheEntry** outModule); +void FindKernelDriverFromAddr(uint64_t addr, const ModuleCacheEntry** outDrive); + + +void CacheModuleAndLoadExternal(const ModuleCacheEntry& moduleCacheEntry); +void CacheModuleKernelAndLoadExternal(const ModuleCacheEntry& kernelDriver); +const FastVector& GetModuleData(); +const FastVector& GetKernelDriver(); + + +// +//void AddModuleToCache() #ifdef TRACY_DEBUGINFOD const uint8_t* GetBuildIdForImage( const char* image, size_t& size ); diff --git a/public/client/TracyFastVector.hpp b/public/client/TracyFastVector.hpp index 38accc926b..9df0466ba7 100644 --- a/public/client/TracyFastVector.hpp +++ b/public/client/TracyFastVector.hpp @@ -56,6 +56,9 @@ class FastVector T& operator[]( size_t idx ) { return m_ptr[idx]; } const T& operator[]( size_t idx ) const { return m_ptr[idx]; } + T& at( size_t idx ) { return m_ptr[idx]; } + const T& at( size_t idx ) const { return m_ptr[idx]; } + T* push_next() { if( m_write == m_end ) AllocMore(); diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 6fe7868093..cee7a6349e 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -3249,6 +3249,24 @@ void Profiler::SendLongString( uint64_t str, const char* ptr, size_t len, QueueT AppendDataUnsafe( ptr, l32 ); } +void Profiler::SendSingleDataPacket( void* ptr, size_t totalSize, PacketDataType type ) +{ + assert( totalSize <= std::numeric_limits::max() ); + + static_assert( sizeof( QueueHeader ) + sizeof( QueueDataPacket ) == QueueDataSize[(int)QueueType::DataPacket], "Size mismatch" ); + + NeedDataSize( QueueDataSize[(int)QueueType::DataPacket] + totalSize ); + + QueueItem item; + tracy::MemWrite( &item.hdr.type, (int)QueueType::DataPacket ); + tracy::MemWrite( &item.packet.packetDataType, type ); + uint16_t dataSize = uint16_t( totalSize ); + tracy::MemWrite( &item.packet.packetSize, dataSize ); + + AppendDataUnsafe( &item, QueueDataSize[(int)QueueType::DataPacket] ); + AppendDataUnsafe( ptr, dataSize ); +} + void Profiler::SendSourceLocation( uint64_t ptr ) { auto srcloc = (const SourceLocationData*)ptr; @@ -3418,7 +3436,8 @@ void Profiler::HandleSymbolQueueItem( const SymbolQueueItem& si ) { case SymbolQueueItemType::CallstackFrame: { - const auto frameData = DecodeCallstackPtr( si.ptr ); + DecodeCallStackPtrStatus unusedStatus; + const auto frameData = DecodeCallstackPtr( si.ptr, &unusedStatus ); auto data = tracy_malloc_fast( sizeof( CallstackEntry ) * frameData.size ); memcpy( data, frameData.data, sizeof( CallstackEntry ) * frameData.size ); TracyLfqPrepare( QueueType::CallstackFrameSize ); @@ -4042,8 +4061,118 @@ void Profiler::ReportTopology() } tracy_free( cpuData ); -#endif -#endif +# endif +# endif +} + +void Profiler::sendModulesCaches() +{ + // We are retrieving modules information from the Tracy Profiler thread + // But the Symbol Worker has ownership. Make sure it is not writing to the cache while we read it. + std::lock_guard mutexguard{ GetModuleCacheMutexForRead() }; + + std::vector sendQueuBuffer; + size_t moduleCount = -1; + + + const FastVector& cacheModule = GetModuleData(); + // Sennd all moduleInfo + for (auto& it : cacheModule) + { + SendModuleInfo(it, &sendQueuBuffer); + } + + + const FastVector& kernelDrivers = GetKernelDriver(); + for (auto& it : kernelDrivers) + { + SendModuleInfo(it, &sendQueuBuffer); + } + +} + + +void Profiler::WriteDebugFieldToPacket(uint8_t** ptr, int* currentPacketSize, const DegugModuleField& degugModuleField) +{ + MemWrite(*ptr, degugModuleField.debugFormat); + *ptr += sizeof(degugModuleField.debugFormat); + *currentPacketSize += sizeof(degugModuleField.debugFormat); + + const uint32_t dataSize = static_cast(degugModuleField.debugDataSize); + + MemWrite(*ptr, dataSize); + *ptr += sizeof(dataSize); + *currentPacketSize += sizeof(dataSize); + + const uint8_t* data = degugModuleField.debugData; + + memcpy(*ptr, data, dataSize); + *ptr += dataSize; + *currentPacketSize += dataSize; + +} + +void Profiler::SendModuleInfo(const ModuleCacheEntry& moduleCacheEntry, std::vector* queuBuffer) +{ + static constexpr int EndOfString = 1; + + const uint32_t moduleNameLength = strlen(moduleCacheEntry.name) + EndOfString; + const uint32_t modulePathLength = strlen(moduleCacheEntry.path) + EndOfString; + + QueueItem item; + tracy::MemWrite(&item.hdr.type, (int)QueueType::DataPacket); + const size_t baseModuleInfo = sizeof(moduleCacheEntry.start) + sizeof(moduleCacheEntry.end) + + sizeof(moduleNameLength) + moduleNameLength + + sizeof(modulePathLength) + modulePathLength + + sizeof(moduleCacheEntry.degugModuleField.debugFormat); + + const uint32_t debugFormatSize = 0 + sizeof(uint32_t) // DebugDataSize + + static_cast(moduleCacheEntry.degugModuleField.debugDataSize); + + queuBuffer->resize(baseModuleInfo + debugFormatSize); + int size = 0; + + uint8_t* ptr = queuBuffer->data(); + + // baseModuleInfo + MemWrite(ptr, moduleCacheEntry.start); + ptr += sizeof(moduleCacheEntry.start); + size += sizeof(moduleCacheEntry.start); + + MemWrite(ptr, moduleCacheEntry.end); + ptr += sizeof(moduleCacheEntry.end); + size += sizeof(moduleCacheEntry.end); + + MemWrite(ptr, moduleNameLength); + ptr += sizeof(moduleNameLength); + size += sizeof(moduleNameLength); + + memcpy(ptr, moduleCacheEntry.name, moduleNameLength); + ptr += moduleNameLength; + size += moduleNameLength; + + MemWrite(ptr, modulePathLength); + ptr += sizeof(modulePathLength); + size += sizeof(modulePathLength); + + memcpy(ptr, moduleCacheEntry.path, modulePathLength); + ptr += modulePathLength; + size += modulePathLength; + + WriteDebugFieldToPacket(&ptr, &size, moduleCacheEntry.degugModuleField); + + + assert(size == queuBuffer->size()); + + SendSingleDataPacket(queuBuffer->data(), queuBuffer->size(), PacketDataType::ModuleInfo); + + QueueItem item2; + tracy::MemWrite(&item2.hdr.type, (int)QueueType::ModuleUpdate); + + static_assert(tracy::QueueDataSize[(int)QueueType::ModuleUpdate] == sizeof(QueueItem), "Size missmatch"); + + NeedDataSize(sizeof(item2)); + AppendData(&item2, sizeof(item2)); } void Profiler::SendCallstack( int32_t depth, const char* skipBefore ) diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index 8d16905860..5b8332afd5 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include "tracy_concurrentqueue.h" #include "tracy_SPSCQueue.h" @@ -777,6 +778,7 @@ class Profiler void SendSecondString( const char* ptr ) { SendSecondString( ptr, strlen( ptr ) ); } void SendSecondString( const char* ptr, size_t len ); + void SendSingleDataPacket(void* ptr, size_t totalSize, PacketDataType type); // Allocated source location data layout: // 2b payload size @@ -918,6 +920,12 @@ class Profiler void CalibrateDelay(); void ReportTopology(); + void sendModulesCaches(); + + void WriteDebugFieldToPacket(uint8_t** ptr, int* currentPacketSize, const DegugModuleField& degugModuleField); + void SendModuleInfo(const ModuleCacheEntry& moduleCacheEntry, std::vector* queuBuffer); + + static tracy_force_inline void SendCallstackSerial( void* ptr ) { if( has_callstack() ) diff --git a/public/common/TracyDebugModulesHeaderFile.hpp b/public/common/TracyDebugModulesHeaderFile.hpp new file mode 100644 index 0000000000..a432ba5fac --- /dev/null +++ b/public/common/TracyDebugModulesHeaderFile.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include + +#pragma pack( push, 1 ) +struct TracyPdbInfo +{ + uint32_t CvSignature; + uint8_t Signature[16]; // GUID + uint32_t Age; +}; +#pragma pack( pop ) + +namespace tracy +{ + + + +#pragma pack( push, 1 ) +struct WindowsDebugData +{ + uint16_t majorVersion; + uint16_t minorVersion; + uint32_t exeDataTimeStamp; + TracyPdbInfo cvInfo; +}; +#pragma pack( pop ) + +enum struct DebugFormat : uint8_t +{ + NoDebugFormat, + PdbDebugFormat, + DwarfDebugFormat, +}; + + +} diff --git a/public/common/TracyProtocol.hpp b/public/common/TracyProtocol.hpp index 839c24e8f4..85367de49c 100644 --- a/public/common/TracyProtocol.hpp +++ b/public/common/TracyProtocol.hpp @@ -48,6 +48,7 @@ enum ServerQuery : uint8_t ServerQueryParameter, ServerQueryFiberName, ServerQueryExternalName, + ServerQueryModuleUpdate, // Items above are high priority. Split order must be preserved. See IsQueryPrio(). ServerQueryDisconnect, ServerQueryCallstackFrame, diff --git a/public/common/TracyQueue.hpp b/public/common/TracyQueue.hpp index c681698a16..52f10baa99 100644 --- a/public/common/TracyQueue.hpp +++ b/public/common/TracyQueue.hpp @@ -68,6 +68,8 @@ enum class QueueType : uint8_t SourceCodeMetadata, FiberEnter, FiberLeave, + ModuleUpdate, + DataPacket, Terminate, KeepAlive, ThreadContext, @@ -693,6 +695,28 @@ struct QueueHeader }; }; +static constexpr size_t MaxModule = 1024; + + +struct QueuModuleInfo +{ + +}; + +enum struct PacketDataType : int +{ + EMPTY = 0, + ModuleInfo, +}; + +static_assert( PacketDataType::EMPTY == (PacketDataType)0, "Empty must be First" ); + +struct QueueDataPacket +{ + PacketDataType packetDataType; + uint16_t packetSize; +}; + struct QueueItem { QueueHeader hdr; @@ -782,6 +806,8 @@ struct QueueItem QueueSourceCodeNotAvailable sourceCodeNotAvailable; QueueFiberEnter fiberEnter; QueueFiberLeave fiberLeave; + QueuModuleInfo moduleInfo; + QueueDataPacket packet; }; }; #pragma pack( pop ) @@ -850,6 +876,8 @@ static constexpr size_t QueueDataSize[] = { sizeof( QueueHeader ) + sizeof( QueueFiberEnter ), sizeof( QueueHeader ) + sizeof( QueueFiberLeave ), // above items must be first + sizeof( QueueItem ), // client modules base address, + sizeof( QueueHeader ) + sizeof( QueueDataPacket ), // DataPacket sizeof( QueueHeader ), // terminate sizeof( QueueHeader ), // keep alive sizeof( QueueHeader ) + sizeof( QueueThreadContext ), diff --git a/public/tracy/Tracy.hpp b/public/tracy/Tracy.hpp index bed511799a..b7b9c27c65 100644 --- a/public/tracy/Tracy.hpp +++ b/public/tracy/Tracy.hpp @@ -13,7 +13,7 @@ #endif #ifndef TracyLine -# define TracyLine __LINE__ +# define TracyLine TracyConcat(__LINE__,U) // MSVC Edit and continue __LINE__ is non-constant. See https://developercommunity.visualstudio.com/t/-line-cannot-be-used-as-an-argument-for-constexpr/195665 #endif #ifndef TRACY_ENABLE @@ -215,7 +215,7 @@ #define ZoneTransientS( varname, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, depth, active ) #define ZoneTransientNS( varname, name, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), depth, active ) -#define ZoneScopedS( depth ) ZoneNamedS( ___tracy_scoped_zone, depth, true ) +#define ZoneScopedS( depth ) ZoneNamedS( ___tracy_scoped_zone, depth, true ) #define ZoneScopedNS( name, depth ) ZoneNamedNS( ___tracy_scoped_zone, name, depth, true ) #define ZoneScopedCS( color, depth ) ZoneNamedCS( ___tracy_scoped_zone, color, depth, true ) #define ZoneScopedNCS( name, color, depth ) ZoneNamedNCS( ___tracy_scoped_zone, name, color, depth, true ) diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index d3de6ba40d..13c83f9176 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -26,11 +26,17 @@ #define ZDICT_STATIC_LINKING_ONLY #include +#include "../client/TracyCallstack.hpp" +#include "TracyDebugModulesHeaderFile.hpp" + #include "../public/common/TracyProtocol.hpp" -#include "../public/common/TracySystem.hpp" -#include "../public/common/TracyYield.hpp" #include "../public/common/TracyStackFrames.hpp" +#include "../public/common/TracySystem.hpp" #include "../public/common/TracyVersion.hpp" +#include "../public/common/TracyYield.hpp" + + +#include "TracyAlign.hpp" #include "TracyFileRead.hpp" #include "TracyFileWrite.hpp" #include "TracyPrint.hpp" @@ -39,6 +45,10 @@ #include "TracyWorker.hpp" #include "tracy_pdqsort.h" + +#define LOCAL_RESOLVE 1 + + namespace tracy { @@ -1643,6 +1653,81 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow } } + auto DeserializeDebugField = [&](DegugModuleField* debugField) + { + DebugFormat debugFormat; + f.Read(debugFormat); + + if (debugFormat == DebugFormat::NoDebugFormat) + { + debugField->debugDataSize = -1; + debugField->debugData = nullptr; + return; + } + + f.Read(debugField->debugDataSize); + debugField->debugData = (uint8_t*)tracy_malloc(debugField->debugDataSize); + f.Read(debugField->debugData, debugField->debugDataSize); + }; + + auto DeserializeMallocedString = [&](char** s) + { + int sLenght = -1; + f.Read(sLenght); + if (sLenght == -1) + return; + + *s = (char*)tracy_malloc(sLenght); + + f.Read(*s, sLenght); + + }; + + uint32_t moduleCount = -1; + f.Read(moduleCount); + + if (moduleCount != -1) + { + for (size_t i = 0; i < moduleCount; i++) + { + ModuleCacheEntry moduleEntry; + f.Read(moduleEntry.start); + f.Read(moduleEntry.end); + DeserializeMallocedString(&moduleEntry.name); + DeserializeMallocedString(&moduleEntry.path); + + DeserializeDebugField(&moduleEntry.degugModuleField); + + CacheModuleAndLoadExternal(moduleEntry); + } + } + + f.Read(moduleCount); + + if (moduleCount != -1) + { + for (size_t i = 0; i < moduleCount; i++) + { + ModuleCacheEntry driver; + f.Read(driver.start); + driver.end = 0; + char* name = nullptr; + DeserializeMallocedString(&name); + driver.name = name; + + char* path = nullptr; + DeserializeMallocedString(&path); + driver.path = path; + + DeserializeDebugField(&driver.degugModuleField); + + CacheModuleKernelAndLoadExternal(driver); + + } + } + + // Post read processing + s_loadProgress.total.store( 0, std::memory_order_relaxed ); m_loadTime = std::chrono::duration_cast( std::chrono::high_resolution_clock::now() - loadStart ).count(); @@ -2275,6 +2360,16 @@ uint64_t Worker::GetSymbolForAddress( uint64_t address, uint32_t& offset ) return it->addr; } +SymbolLocation Worker::GetSymbolLocFromSysAdrees( uint64_t address ) +{ + DoPostponedSymbols(); + auto it = std::lower_bound( m_data.symbolLoc.begin(), m_data.symbolLoc.end(), address, + []( const auto& l, const auto& r ) { return l.addr + l.len < r; } ); + if( it == m_data.symbolLoc.end() || address < it->addr ) return {}; + + return *it; +} + uint64_t Worker::GetInlineSymbolForAddress( uint64_t address ) const { auto it = m_data.codeSymbolMap.find( address ); @@ -2625,7 +2720,8 @@ const SymbolStats* Worker::GetSymbolStats( uint64_t symAddr ) const } } -const unordered_flat_map* Worker::GetSymbolInstructionPointers( uint64_t symAddr ) const +const unordered_flat_map* +Worker::GetSymbolInstructionPointers( uint64_t symAddr ) const { assert( AreCallstackSamplesReady() ); auto it = m_data.instructionPointersMap.find( symAddr ); @@ -2708,6 +2804,11 @@ void Worker::Exec() switch( handshake ) { case HandshakeWelcome: + +#ifdef LOCAL_RESOLVE + InitCallstack(); +#endif // LOCAL_RESOLVE + break; case HandshakeProtocolMismatch: case HandshakeNotAvailable: @@ -2902,6 +3003,11 @@ void Worker::Exec() } close: + +#ifdef LOCAL_RESOLVE + EndCallstack(); +#endif // LOCAL_RESOLVE + Shutdown(); m_netWriteCv.notify_one(); m_sock.Close(); @@ -3085,6 +3191,15 @@ void Worker::DispatchFailure( const QueueItem& ev, const char*& ptr ) AddSecondString( ptr, sz ); ptr += sz; break; + case QueueType::DataPacket: + { + const QueueDataPacket* packet = reinterpret_cast( ptr ); + ptr += sizeof( QueueHeader ); + ptr += sizeof( QueueDataPacket ); + AddDataPacket( reinterpret_cast( ptr ), packet->packetSize, packet->packetDataType ); + ptr += packet->packetSize; + } + break; default: ptr += QueueDataSize[ev.hdr.idx]; switch( ev.hdr.type ) @@ -3280,6 +3395,15 @@ bool Worker::DispatchProcess( const QueueItem& ev, const char*& ptr ) AddSecondString( ptr, sz ); ptr += sz; return true; + case QueueType::DataPacket: + { + ptr += sizeof(QueueHeader); + const QueueDataPacket* packet = reinterpret_cast(ptr); + ptr += sizeof(QueueDataPacket); + AddDataPacket(reinterpret_cast(ptr), packet->packetSize, packet->packetDataType); + ptr += packet->packetSize; + return true; + } default: ptr += QueueDataSize[ev.hdr.idx]; return Process( ev ); @@ -3782,6 +3906,30 @@ void Worker::AddSecondString( const char* str, size_t sz ) m_pendingSecondString = StoreString( str, sz ); } +void Worker::AddDataPacketFailure( const void* data, size_t size, PacketDataType dataType ) +{ + m_pendingDataPacket.type = dataType; + m_pendingDataPacket.data.resize( size ); + memcpy( m_pendingDataPacket.data.data(), data, size ); +} + +void Worker::AddDataPacket( const void* data, size_t size, PacketDataType dataType ) +{ + assert( m_pendingDataPacket.type == PacketDataType::EMPTY && m_pendingDataPacket.data.size() == 0 && + dataType != PacketDataType::EMPTY ); + + m_pendingDataPacket.type = dataType; + m_pendingDataPacket.data.resize( size ); + memcpy( m_pendingDataPacket.data.data(), data, size ); + +#if 0 + int d[100]; + for( size_t i = 0; i < size / sizeof( int ); i++ ) + d[i] = *reinterpret_cast( &m_pendingDataPacket.data[i * 4] ); + +#endif +} + void Worker::AddExternalName( uint64_t ptr, const char* str, size_t sz ) { assert( m_pendingExternalNames > 0 ); @@ -3862,11 +4010,8 @@ void Worker::AddSymbolCode( uint64_t ptr, const char* data, size_t sz ) { const auto& op = insn[i]; const auto addr = op.address; - if( m_data.callstackFrameMap.find( PackPointer( addr ) ) == m_data.callstackFrameMap.end() ) - { - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, addr ); - } + TryResolveCallStackIfNeeded(PackPointer(addr)); + uint64_t callAddr = 0; const auto& detail = *op.detail; @@ -3902,10 +4047,9 @@ void Worker::AddSymbolCode( uint64_t ptr, const char* data, size_t sz ) if( callAddr != 0 ) break; } } - if( callAddr != 0 && m_data.callstackFrameMap.find( PackPointer( callAddr ) ) == m_data.callstackFrameMap.end() ) + if( callAddr != 0) { - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, callAddr ); + TryResolveCallStackIfNeeded(PackPointer(addr)); } } cs_free( insn, cnt ); @@ -3942,6 +4086,58 @@ uint64_t Worker::GetCanonicalPointer( const CallstackFrameId& id ) const return ( id.idx & 0x3FFFFFFFFFFFFFFF ) | ( ( id.idx & 0x3000000000000000 ) << 2 ); } +void Worker::TryResolveCallStackIfNeeded(CallstackFrameId frameId) +{ + if (m_data.callstackFrameMap.count(frameId) != 0) + { + return; + } + + m_pendingCallstackFrames++; + uint64_t symbolAddress = GetCanonicalPointer(frameId); + if (!m_resolveSymbolLocally) + { + Query(ServerQueryCallstackFrame, symbolAddress); + return; + } + + + DecodeCallStackPtrStatus status; + CallstackEntryData outCallStack = DecodeCallstackPtr(symbolAddress, &status); + + if (status != DecodeCallStackPtrStatus::Success) + { + //Query(ServerQueryModuleUpdate, symbolAddress, 0); + } + + + const uint32_t imageNameIdx = StoreString(outCallStack.imageName).idx; + assert(outCallStack.size <= 255); + const QueueCallstackFrameSize queueCallstackFrameSize = + { + .ptr = symbolAddress, + .size = outCallStack.size, + }; + + ProcessCallstackFrameSize(queueCallstackFrameSize, imageNameIdx); + + // we know that for the best we have inline information + for (size_t i = 0; i < queueCallstackFrameSize.size; i++) + { + const auto& frame = outCallStack.data[i]; + ProcessCallstackFrame(frame.line, frame.symAddr, frame.symLen, + StoreString(frame.name).idx, StoreString(frame.file).idx, true); + + // We copied it, now free the content + tracy_free_fast((void*)frame.name); + tracy_free_fast((void*)frame.file); + } + + assert(m_pendingCallstackSubframes == 0); + + +} + void Worker::AddCallstackPayload( const char* _data, size_t _sz ) { assert( m_pendingCallstackId == 0 ); @@ -3969,14 +4165,10 @@ void Worker::AddCallstackPayload( const char* _data, size_t _sz ) m_data.callstackMap.emplace( arr, idx ); m_data.callstackPayload.push_back( arr ); - for( auto& frame : *arr ) + + for (CallstackFrameId frameId : *arr) { - auto fit = m_data.callstackFrameMap.find( frame ); - if( fit == m_data.callstackFrameMap.end() ) - { - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, GetCanonicalPointer( frame ) ); - } + TryResolveCallStackIfNeeded(frameId); } } else @@ -4064,12 +4256,7 @@ void Worker::AddCallstackAllocPayload( const char* data ) for( auto& frame : *arr ) { - auto fit = m_data.callstackFrameMap.find( frame ); - if( fit == m_data.callstackFrameMap.end() ) - { - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, GetCanonicalPointer( frame ) ); - } + TryResolveCallStackIfNeeded(frame); } } else @@ -4408,6 +4595,21 @@ uint32_t Worker::GetSecondStringIdx() return idx; } +void Worker::AccessPendingData( uint8_t** ptr, size_t* sz, PacketDataType* type ) +{ + assert( m_pendingDataPacket.type != PacketDataType::EMPTY && m_pendingDataPacket.data.size() != 0 ); + + *ptr = m_pendingDataPacket.data.data(); + *sz = m_pendingDataPacket.data.size(); + *type = m_pendingDataPacket.type; +} + +void Worker::FreePendingData() +{ + m_pendingDataPacket.data.clear(); + m_pendingDataPacket.type = PacketDataType::EMPTY; +} + StringLocation Worker::StoreString( const char* str, size_t sz ) { StringLocation ret; @@ -4431,10 +4633,18 @@ StringLocation Worker::StoreString( const char* str, size_t sz ) return ret; } +StringLocation Worker::StoreString(const char* str) +{ + return StoreString(str, strlen(str)); +} + bool Worker::Process( const QueueItem& ev ) { switch( ev.hdr.type ) { + case QueueType::ModuleUpdate: + DispatchModuleInfo( ev.moduleInfo ); + break; case QueueType::ThreadContext: ProcessThreadContext( ev.threadCtx ); break; @@ -6488,7 +6698,7 @@ void Worker::ProcessCallstackSampleContextSwitch( const QueueCallstackSample& ev td.ctxSwitchSamples.push_back( sd ); } -void Worker::ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev ) +void Worker::ProcessCallstackFrameSize(const QueueCallstackFrameSize& ev, uint32_t imageNameIdx) { assert( !m_callstackFrameStaging ); assert( m_pendingCallstackSubframes == 0 ); @@ -6499,27 +6709,35 @@ void Worker::ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev ) m_data.newFramesWereReceived = true; #endif - const auto idx = GetSingleStringIdx(); - // Frames may be duplicated due to recursion - auto fmit = m_data.callstackFrameMap.find( PackPointer( ev.ptr ) ); - if( fmit == m_data.callstackFrameMap.end() ) + auto fmit = m_data.callstackFrameMap.find(PackPointer(ev.ptr)); + if (fmit == m_data.callstackFrameMap.end()) { m_callstackFrameStaging = m_slab.Alloc(); m_callstackFrameStaging->size = ev.size; - m_callstackFrameStaging->data = m_slab.Alloc( ev.size ); - m_callstackFrameStaging->imageName = StringIdx( idx ); + m_callstackFrameStaging->data = m_slab.Alloc(ev.size); + m_callstackFrameStaging->imageName = StringIdx(imageNameIdx); m_callstackFrameStagingPtr = ev.ptr; } } -void Worker::ProcessCallstackFrame( const QueueCallstackFrame& ev, bool querySymbols ) +void Worker::ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev ) { - assert( m_pendingCallstackSubframes > 0 ); + ProcessCallstackFrameSize(ev, GetSingleStringIdx()); +} - const auto nitidx = GetSingleStringIdx(); - const auto fitidx = GetSecondStringIdx(); +void Worker::ProcessCallstackFrame(const QueueCallstackFrame& ev, bool querySymbols) +{ + const uint32_t nitidx = GetSingleStringIdx(); + const uint32_t fitidx = GetSecondStringIdx(); + + ProcessCallstackFrame(ev.line, ev.symAddr, ev.symLen, nitidx, fitidx, querySymbols); +} + +void Worker::ProcessCallstackFrame(uint32_t line, uint64_t symAddr, uint32_t symLen, uint32_t nitidx, uint32_t fitidx, bool querySymbols) +{ + assert( m_pendingCallstackSubframes > 0 ); if( m_callstackFrameStaging ) { @@ -6550,13 +6768,18 @@ void Worker::ProcessCallstackFrame( const QueueCallstackFrame& ev, bool querySym const auto name = StringIdx( nitidx ); m_callstackFrameStaging->data[idx].name = name; m_callstackFrameStaging->data[idx].file = file; - m_callstackFrameStaging->data[idx].line = ev.line; - m_callstackFrameStaging->data[idx].symAddr = ev.symAddr; + m_callstackFrameStaging->data[idx].line = line; + m_callstackFrameStaging->data[idx].symAddr = symAddr; - if( querySymbols && ev.symAddr != 0 && m_data.symbolMap.find( ev.symAddr ) == m_data.symbolMap.end() && m_pendingSymbols.find( ev.symAddr ) == m_pendingSymbols.end() ) + if(querySymbols && symAddr!= 0 && m_data.symbolMap.find(symAddr) == m_data.symbolMap.end() && + m_pendingSymbols.find(symAddr) == m_pendingSymbols.end()) { - m_pendingSymbols.emplace( ev.symAddr, SymbolPending { name, m_callstackFrameStaging->imageName, file, ev.line, ev.symLen, idx < m_callstackFrameStaging->size - 1 } ); - Query( ServerQuerySymbol, ev.symAddr ); + m_pendingSymbols.emplace(symAddr, + SymbolPending{ name, m_callstackFrameStaging->imageName, file, line, symLen, + idx < m_callstackFrameStaging->size - 1 } ); + + + Query( ServerQuerySymbol, symAddr ); } StringRef ref( StringRef::Idx, fitidx ); @@ -6564,16 +6787,22 @@ void Worker::ProcessCallstackFrame( const QueueCallstackFrame& ev, bool querySym if( cit == m_checkedFileStrings.end() ) CacheSource( ref, m_callstackFrameStaging->imageName ); const auto frameId = PackPointer( m_callstackFrameStagingPtr ); + #ifndef TRACY_NO_STATISTICS + + auto it = m_data.pendingInstructionPointers.find( frameId ); if( it != m_data.pendingInstructionPointers.end() ) { - if( ev.symAddr != 0 ) + if( symAddr != 0 ) { - auto sit = m_data.instructionPointersMap.find( ev.symAddr ); + auto sit = m_data.instructionPointersMap.find( symAddr ); if( sit == m_data.instructionPointersMap.end() ) { - m_data.instructionPointersMap.emplace( ev.symAddr, unordered_flat_map { { it->first, it->second } } ); + m_data.instructionPointersMap.emplace( + symAddr, + unordered_flat_map{ + { it->first, it->second } } ); } else { @@ -6583,16 +6812,19 @@ void Worker::ProcessCallstackFrame( const QueueCallstackFrame& ev, bool querySym } m_data.pendingInstructionPointers.erase( it ); } + + auto pit = m_data.pendingSymbolSamples.find( frameId ); if( pit != m_data.pendingSymbolSamples.end() ) { - if( ev.symAddr != 0 ) + if( symAddr != 0 ) { - auto sit = m_data.symbolSamples.find( ev.symAddr ); + auto sit = m_data.symbolSamples.find( symAddr ); if( sit == m_data.symbolSamples.end() ) - { - pdqsort_branchless( pit->second.begin(), pit->second.end(), [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs.time.Val(); } ); - m_data.symbolSamples.emplace( ev.symAddr, std::move( pit->second ) ); + { + pdqsort_branchless( pit->second.begin(), pit->second.end(), []( const auto& lhs, const auto& rhs ) + { return lhs.time.Val() < rhs.time.Val(); } ); + m_data.symbolSamples.emplace( symAddr, std::move( pit->second ) ); } else { @@ -8364,6 +8596,64 @@ void Worker::Write( FileWrite& f, bool fiDict ) f.Write( &v.second.len, sizeof( v.second.len ) ); f.Write( v.second.data, v.second.len ); } + + + + auto SerializeDebugModuleField = [&](const DegugModuleField& debugField) + { + f.Write(&debugField.debugFormat, sizeof(debugField.debugFormat)); + if (debugField.debugFormat == DebugFormat::NoDebugFormat) + return; + + + f.Write(&debugField.debugDataSize, sizeof(debugField.debugDataSize)); + f.Write(debugField.debugData, debugField.debugDataSize); + + }; + + auto SerializeString = [&](const char* s) + { + int sLenght = s != nullptr ? strlen(s) + 1 : -1; + f.Write(&sLenght, sizeof(sLenght)); + + if (sLenght == -1) + return; + + f.Write(s, sLenght); + }; + + const FastVector& cacheModule = GetModuleData(); + uint32_t moduleCount = static_cast(cacheModule.size()); + f.Write(&moduleCount, sizeof(moduleCount)); + + for (const ModuleCacheEntry& it : cacheModule) + { + const ModuleCacheEntry& moduleCacheEntry = it; + f.Write(&moduleCacheEntry.start, sizeof(moduleCacheEntry.start)); + f.Write(&moduleCacheEntry.end, sizeof(moduleCacheEntry.end)); + + SerializeString(moduleCacheEntry.name); + SerializeString(moduleCacheEntry.path); + + SerializeDebugModuleField(moduleCacheEntry.degugModuleField); + } + + + const FastVector& kernelDrivers = GetKernelDriver(); + moduleCount = static_cast(kernelDrivers.size()); + f.Write(&moduleCount, sizeof(moduleCount)); + + for (const ModuleCacheEntry& it : kernelDrivers) + { + f.Write(&it.start, sizeof(it.start)); + assert(it.end == 0 && "kernel end should be zero"); + + SerializeString(it.name); + SerializeString(it.path); + + SerializeDebugModuleField(it.degugModuleField); + } + } void Worker::WriteTimeline( FileWrite& f, const Vector>& vec, int64_t& refTime ) @@ -8612,4 +8902,83 @@ void Worker::CacheSourceFiles() } } +void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) +{ + uint8_t* ptrToData = nullptr; + size_t s = 0; + PacketDataType dataType; + AccessPendingData( &ptrToData, &s, &dataType ); + + switch( dataType ) + { + case PacketDataType::ModuleInfo: + { + uint64_t baseAddress = MemRead( ptrToData ); + ptrToData += sizeof( baseAddress ); + + uint64_t end = MemRead(ptrToData); + ptrToData += sizeof(end); + + uint32_t moduleNameSize = MemRead( ptrToData ); + ptrToData += sizeof(moduleNameSize); + + char* moduleName = (char*)tracy_malloc(moduleNameSize); + memcpy(moduleName, ptrToData, moduleNameSize); + ptrToData += moduleNameSize; + StringLocation moduleNameStringLocation = StoreString(moduleName); + assert((char)(moduleName[moduleNameSize - 1]) == '\0' && "missing end of string"); + + uint32_t modulePathSize = MemRead(ptrToData); + ptrToData += sizeof(modulePathSize); + + char* modulePath = (char*)tracy_malloc(modulePathSize); + memcpy( modulePath, ptrToData, modulePathSize ); + ptrToData += modulePathSize; + StringLocation modulePathStringLocation = StoreString(modulePath); + assert((char)(modulePath[modulePathSize - 1]) == '\0' && "missing end of string"); + + + DebugFormat debugFormat = MemRead( ptrToData ); + ptrToData += sizeof( DebugFormat ); + + ModuleCacheEntry moduleCacheEntry = + { + .start = baseAddress, + .end = end, + .name = moduleName, + .path = modulePath, + }; + moduleCacheEntry.degugModuleField.debugFormat = debugFormat; + + if (debugFormat == DebugFormat::PdbDebugFormat) + { + + uint32_t debugFormatSize = MemRead( ptrToData ); + ptrToData += sizeof( debugFormatSize ); + + uint8_t* debugData = (uint8_t*)tracy_malloc(debugFormatSize); + memcpy(debugData, ptrToData, debugFormatSize ); + ptrToData += debugFormatSize; + + DegugModuleField& degugModuleField = moduleCacheEntry.degugModuleField; + + degugModuleField.debugData = debugData; + degugModuleField.debugDataSize = debugFormatSize; + + assert((char)(debugData[debugFormatSize - 1]) == '\0' && "missing end of string"); + + } + + CacheModuleAndLoadExternal(moduleCacheEntry); + + break; + } + default: + break; + } + + FreePendingData(); } + + +} \ No newline at end of file diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index f072104136..e35a0abb36 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -17,6 +17,8 @@ #include "../public/common/TracyQueue.hpp" #include "../public/common/TracyProtocol.hpp" #include "../public/common/TracySocket.hpp" +#include "../public/common/TracyDebugModulesHeaderFile.hpp" + #include "tracy_robin_hood.h" #include "TracyEvent.hpp" #include "TracyShortPtr.hpp" @@ -285,6 +287,7 @@ class Worker StringDiscovery plots; Vector threads; Vector zoneExtra; + Vector modulesBaseAddress; MemData* memory; unordered_flat_map memNameMap; uint64_t zonesCnt = 0; @@ -431,6 +434,12 @@ class Worker const char* image; uint32_t csz; }; + + struct PendingDataPacket + { + PacketDataType type; + std::vector data; + }; public: enum class Failure @@ -553,6 +562,8 @@ class Worker const char* GetSymbolCode( uint64_t sym, uint32_t& len ) const; uint64_t GetSymbolForAddress( uint64_t address ); uint64_t GetSymbolForAddress( uint64_t address, uint32_t& offset ); + SymbolLocation GetSymbolLocFromSysAdrees( uint64_t address); + uint64_t GetInlineSymbolForAddress( uint64_t address ) const; bool HasInlineSymbolAddresses() const { return !m_data.codeSymbolMap.empty(); } StringIdx GetLocationForAddress( uint64_t address, uint32_t& line ) const; @@ -678,6 +689,7 @@ class Worker void CacheSourceFiles(); StringLocation StoreString(const char* str, size_t sz); + StringLocation StoreString(const char* str); std::vector& GetPendingThreadHints() { return m_pendingThreadHints; } void ClearPendingThreadHints() { m_pendingThreadHints.clear(); } @@ -755,8 +767,11 @@ class Worker tracy_force_inline void ProcessCallstack(); tracy_force_inline void ProcessCallstackSample( const QueueCallstackSample& ev ); tracy_force_inline void ProcessCallstackSampleContextSwitch( const QueueCallstackSample& ev ); + tracy_force_inline void ProcessCallstackFrameSize(const QueueCallstackFrameSize& ev, uint32_t imageNameIdx); tracy_force_inline void ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev ); tracy_force_inline void ProcessCallstackFrame( const QueueCallstackFrame& ev, bool querySymbols ); + tracy_force_inline void ProcessCallstackFrame(uint32_t line, uint64_t symAddr, uint32_t symLen, uint32_t nitidx, uint32_t fitidx, bool querySymbols); + tracy_force_inline void ProcessSymbolInformation( const QueueSymbolInformation& ev ); tracy_force_inline void ProcessCrashReport( const QueueCrashReport& ev ); tracy_force_inline void ProcessSysTime( const QueueSysTime& ev ); @@ -778,6 +793,8 @@ class Worker tracy_force_inline void ProcessFiberEnter( const QueueFiberEnter& ev ); tracy_force_inline void ProcessFiberLeave( const QueueFiberLeave& ev ); + tracy_force_inline void DispatchModuleInfo( const QueuModuleInfo& ev); + tracy_force_inline ZoneEvent* AllocZoneEvent(); tracy_force_inline void ProcessZoneBeginImpl( ZoneEvent* zone, const QueueZoneBegin& ev ); tracy_force_inline void ProcessZoneBeginAllocSrcLocImpl( ZoneEvent* zone, const QueueZoneBeginLean& ev ); @@ -887,12 +904,14 @@ class Worker void AddSingleString( const char* str, size_t sz ); void AddSingleStringFailure( const char* str, size_t sz ); void AddSecondString( const char* str, size_t sz ); + void AddDataPacket( const void* data, size_t size , PacketDataType dataType); void AddExternalName( uint64_t ptr, const char* str, size_t sz ); void AddExternalThreadName( uint64_t ptr, const char* str, size_t sz ); void AddFrameImageData( const char* data, size_t sz ); void AddSymbolCode( uint64_t ptr, const char* data, size_t sz ); void AddSourceCode( uint32_t id, const char* data, size_t sz ); + void TryResolveCallStackIfNeeded(CallstackFrameId frameId); tracy_force_inline void AddCallstackPayload( const char* data, size_t sz ); tracy_force_inline void AddCallstackAllocPayload( const char* data ); uint32_t MergeCallstacks( uint32_t first, uint32_t second ); @@ -913,6 +932,10 @@ class Worker uint32_t GetSingleStringIdx(); uint32_t GetSecondStringIdx(); + + void AccessPendingData( uint8_t** ptr, size_t* sz, PacketDataType* type ); + void FreePendingData(); + const ContextSwitch* const GetContextSwitchDataImpl( uint64_t thread ); void CacheSource( const StringRef& str, const StringIdx& image = StringIdx() ); @@ -1006,6 +1029,7 @@ class Worker bool m_identifySamples = false; bool m_inconsistentSamples; bool m_allowStringModification = false; + bool m_resolveSymbolLocally = true; short_ptr m_gpuCtxMap[256]; uint32_t m_pendingCallstackId = 0; @@ -1020,6 +1044,7 @@ class Worker unordered_flat_set m_checkedFileStrings; StringLocation m_pendingSingleString = {}; StringLocation m_pendingSecondString = {}; + PendingDataPacket m_pendingDataPacket = {}; uint32_t m_pendingStrings; uint32_t m_pendingThreads; From 7ef3fe328a943388c97f4fad2b467e65f0718408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sat, 8 Mar 2025 18:06:56 +0100 Subject: [PATCH 04/40] Move TracyCallstack (and dependencies) to common/ folder --- CMakeLists.txt | 10 +++++----- cmake/server.cmake | 1 + meson.build | 10 +++++----- public/TracyClient.cpp | 3 +-- public/client/TracyKCore.cpp | 2 +- public/client/TracyKCore.hpp | 2 +- public/client/TracyOverride.cpp | 2 +- public/client/TracyProfiler.cpp | 4 ++-- public/client/TracyProfiler.hpp | 4 ++-- public/client/TracyRingBuffer.hpp | 2 +- public/client/TracyScoped.hpp | 2 +- public/client/TracySysPower.cpp | 2 +- public/client/TracySysPower.hpp | 2 +- public/client/TracySysTrace.cpp | 4 ++-- public/{client => common}/TracyCallstack.cpp | 12 +++++++----- public/{client => common}/TracyCallstack.h | 0 public/{client => common}/TracyCallstack.hpp | 2 +- public/{client => common}/TracyDebug.hpp | 0 public/{client => common}/TracyFastVector.hpp | 1 + public/{client => common}/TracyStringHelpers.hpp | 0 public/libbacktrace/elf.cpp | 2 +- public/tracy/TracyD3D11.hpp | 2 +- public/tracy/TracyD3D12.hpp | 2 +- public/tracy/TracyMetal.hmm | 2 +- public/tracy/TracyOpenCL.hpp | 2 +- public/tracy/TracyOpenGL.hpp | 2 +- public/tracy/TracyVulkan.hpp | 2 +- server/TracyWorker.cpp | 2 +- 28 files changed, 42 insertions(+), 39 deletions(-) rename public/{client => common}/TracyCallstack.cpp (99%) rename public/{client => common}/TracyCallstack.h (100%) rename public/{client => common}/TracyCallstack.hpp (99%) rename public/{client => common}/TracyDebug.hpp (100%) rename public/{client => common}/TracyFastVector.hpp (99%) rename public/{client => common}/TracyStringHelpers.hpp (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b529a7442..26ad548eb5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,17 +177,12 @@ set(client_includes ${TRACY_PUBLIC_DIR}/client/tracy_SPSCQueue.h ${TRACY_PUBLIC_DIR}/client/TracyKCore.hpp ${TRACY_PUBLIC_DIR}/client/TracyArmCpuTable.hpp - ${TRACY_PUBLIC_DIR}/client/TracyCallstack.h - ${TRACY_PUBLIC_DIR}/client/TracyCallstack.hpp ${TRACY_PUBLIC_DIR}/client/TracyCpuid.hpp - ${TRACY_PUBLIC_DIR}/client/TracyDebug.hpp ${TRACY_PUBLIC_DIR}/client/TracyDxt1.hpp - ${TRACY_PUBLIC_DIR}/client/TracyFastVector.hpp ${TRACY_PUBLIC_DIR}/client/TracyLock.hpp ${TRACY_PUBLIC_DIR}/client/TracyProfiler.hpp ${TRACY_PUBLIC_DIR}/client/TracyRingBuffer.hpp ${TRACY_PUBLIC_DIR}/client/TracyScoped.hpp - ${TRACY_PUBLIC_DIR}/client/TracyStringHelpers.hpp ${TRACY_PUBLIC_DIR}/client/TracySysPower.hpp ${TRACY_PUBLIC_DIR}/client/TracySysTime.hpp ${TRACY_PUBLIC_DIR}/client/TracySysTrace.hpp @@ -199,13 +194,18 @@ set(common_includes ${TRACY_PUBLIC_DIR}/common/TracyAlign.hpp ${TRACY_PUBLIC_DIR}/common/TracyAlloc.hpp ${TRACY_PUBLIC_DIR}/common/TracyApi.h + ${TRACY_PUBLIC_DIR}/common/TracyCallstack.h + ${TRACY_PUBLIC_DIR}/common/TracyCallstack.hpp ${TRACY_PUBLIC_DIR}/common/TracyColor.hpp + ${TRACY_PUBLIC_DIR}/common/TracyDebug.hpp + ${TRACY_PUBLIC_DIR}/common/TracyFastVector.hpp ${TRACY_PUBLIC_DIR}/common/TracyForceInline.hpp ${TRACY_PUBLIC_DIR}/common/TracyMutex.hpp ${TRACY_PUBLIC_DIR}/common/TracyProtocol.hpp ${TRACY_PUBLIC_DIR}/common/TracyQueue.hpp ${TRACY_PUBLIC_DIR}/common/TracySocket.hpp ${TRACY_PUBLIC_DIR}/common/TracyStackFrames.hpp + ${TRACY_PUBLIC_DIR}/common/TracyStringHelpers.hpp ${TRACY_PUBLIC_DIR}/common/TracySystem.hpp ${TRACY_PUBLIC_DIR}/common/TracyUwp.hpp ${TRACY_PUBLIC_DIR}/common/TracyYield.hpp) diff --git a/cmake/server.cmake b/cmake/server.cmake index a76d1c1340..67dae697e7 100644 --- a/cmake/server.cmake +++ b/cmake/server.cmake @@ -3,6 +3,7 @@ set(TRACY_COMMON_DIR ${CMAKE_CURRENT_LIST_DIR}/../public/common) set(TRACY_COMMON_SOURCES tracy_lz4.cpp tracy_lz4hc.cpp + TracyCallstack.cpp TracySocket.cpp TracyStackFrames.cpp TracySystem.cpp diff --git a/meson.build b/meson.build index c86625dcaf..25cb3bbfac 100644 --- a/meson.build +++ b/meson.build @@ -145,17 +145,12 @@ client_includes = [ 'public/client/tracy_rpmalloc.hpp', 'public/client/tracy_SPSCQueue.h', 'public/client/TracyArmCpuTable.hpp', - 'public/client/TracyCallstack.h', - 'public/client/TracyCallstack.hpp', - 'public/client/TracyDebug.hpp', 'public/client/TracyDxt1.hpp', - 'public/client/TracyFastVector.hpp', 'public/client/TracyKCore.hpp', 'public/client/TracyLock.hpp', 'public/client/TracyProfiler.hpp', 'public/client/TracyRingBuffer.hpp', 'public/client/TracyScoped.hpp', - 'public/client/TracyStringHelpers.hpp', 'public/client/TracySysPower.hpp', 'public/client/TracySysTime.hpp', 'public/client/TracySysTrace.hpp', @@ -168,13 +163,18 @@ common_includes = [ 'public/common/TracyAlign.hpp', 'public/common/TracyAlloc.hpp', 'public/common/TracyApi.h', + 'public/common/TracyCallstack.h', + 'public/common/TracyCallstack.hpp', 'public/common/TracyColor.hpp', + 'public/common/TracyDebug.hpp', + 'public/common/TracyFastVector.hpp', 'public/common/TracyForceInline.hpp', 'public/common/TracyMutex.hpp', 'public/common/TracyProtocol.hpp', 'public/common/TracyQueue.hpp', 'public/common/TracySocket.hpp', 'public/common/TracyStackFrames.hpp', + 'public/common/TracyStringHelpers.hpp', 'public/common/TracySystem.hpp', 'public/common/TracyUwp.hpp', 'public/common/TracyYield.hpp' diff --git a/public/TracyClient.cpp b/public/TracyClient.cpp index a786bef362..70bd17456e 100644 --- a/public/TracyClient.cpp +++ b/public/TracyClient.cpp @@ -21,7 +21,7 @@ #include "common/tracy_lz4.cpp" #include "client/TracyProfiler.cpp" -#include "client/TracyCallstack.cpp" +#include "common/TracyCallstack.cpp" #include "client/TracySysPower.cpp" #include "client/TracySysTime.cpp" #include "client/TracySysTrace.cpp" @@ -31,7 +31,6 @@ #include "client/TracyAlloc.cpp" #include "client/TracyOverride.cpp" #include "client/TracyKCore.cpp" -#include "common/TracyModulesPdbSeacher.cpp" #if defined(TRACY_HAS_CALLSTACK) # if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 diff --git a/public/client/TracyKCore.cpp b/public/client/TracyKCore.cpp index 09d51d117a..b4d06fbd53 100644 --- a/public/client/TracyKCore.cpp +++ b/public/client/TracyKCore.cpp @@ -6,7 +6,7 @@ #include #include -#include "TracyDebug.hpp" +#include "../common/TracyDebug.hpp" #include "TracyKCore.hpp" #include "../common/TracyAlloc.hpp" diff --git a/public/client/TracyKCore.hpp b/public/client/TracyKCore.hpp index 437e172c23..15999c6c35 100644 --- a/public/client/TracyKCore.hpp +++ b/public/client/TracyKCore.hpp @@ -5,7 +5,7 @@ #include -#include "TracyFastVector.hpp" +#include "../common/TracyFastVector.hpp" namespace tracy { diff --git a/public/client/TracyOverride.cpp b/public/client/TracyOverride.cpp index 591508a7ff..871cbe031c 100644 --- a/public/client/TracyOverride.cpp +++ b/public/client/TracyOverride.cpp @@ -1,6 +1,6 @@ #ifdef TRACY_ENABLE # ifdef __linux__ -# include "TracyDebug.hpp" +# include "../common/TracyDebug.hpp" # ifdef TRACY_VERBOSE # include # include diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index cee7a6349e..3840b7af97 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -71,8 +71,8 @@ #include "../common/TracyYield.hpp" #include "../common/tracy_lz4.hpp" #include "tracy_rpmalloc.hpp" -#include "TracyCallstack.hpp" -#include "TracyDebug.hpp" +#include "../common/TracyCallstack.hpp" +#include "../common/TracyDebug.hpp" #include "TracyDxt1.hpp" #include "TracyScoped.hpp" #include "TracyProfiler.hpp" diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index 5b8332afd5..96f751df69 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -10,11 +10,11 @@ #include "tracy_concurrentqueue.h" #include "tracy_SPSCQueue.h" -#include "TracyCallstack.hpp" #include "TracyKCore.hpp" #include "TracySysPower.hpp" #include "TracySysTime.hpp" -#include "TracyFastVector.hpp" +#include "../common/TracyCallstack.hpp" +#include "../common/TracyFastVector.hpp" #include "../common/TracyQueue.hpp" #include "../common/TracyAlign.hpp" #include "../common/TracyAlloc.hpp" diff --git a/public/client/TracyRingBuffer.hpp b/public/client/TracyRingBuffer.hpp index e9100e2d8b..99bb06c3ef 100644 --- a/public/client/TracyRingBuffer.hpp +++ b/public/client/TracyRingBuffer.hpp @@ -8,7 +8,7 @@ #include #include -#include "TracyDebug.hpp" +#include "../common/TracyDebug.hpp" namespace tracy { diff --git a/public/client/TracyScoped.hpp b/public/client/TracyScoped.hpp index 7f9256d8c3..9f63bcef55 100644 --- a/public/client/TracyScoped.hpp +++ b/public/client/TracyScoped.hpp @@ -10,7 +10,7 @@ #include "../common/TracyAlign.hpp" #include "../common/TracyAlloc.hpp" #include "TracyProfiler.hpp" -#include "TracyCallstack.hpp" +#include "../common/TracyCallstack.hpp" namespace tracy { diff --git a/public/client/TracySysPower.cpp b/public/client/TracySysPower.cpp index 6ad1d64783..67749fcc5a 100644 --- a/public/client/TracySysPower.cpp +++ b/public/client/TracySysPower.cpp @@ -9,7 +9,7 @@ #include #include -#include "TracyDebug.hpp" +#include "../common/TracyDebug.hpp" #include "TracyProfiler.hpp" #include "../common/TracyAlloc.hpp" diff --git a/public/client/TracySysPower.hpp b/public/client/TracySysPower.hpp index 210123bce4..01db32724c 100644 --- a/public/client/TracySysPower.hpp +++ b/public/client/TracySysPower.hpp @@ -10,7 +10,7 @@ #include #include -#include "TracyFastVector.hpp" +#include "../common/TracyFastVector.hpp" namespace tracy { diff --git a/public/client/TracySysTrace.cpp b/public/client/TracySysTrace.cpp index 5827a9922d..2fb36fd01f 100644 --- a/public/client/TracySysTrace.cpp +++ b/public/client/TracySysTrace.cpp @@ -1,5 +1,5 @@ -#include "TracyDebug.hpp" -#include "TracyStringHelpers.hpp" +#include "../common/TracyDebug.hpp" +#include "../common/TracyStringHelpers.hpp" #include "TracySysTrace.hpp" #include "../common/TracySystem.hpp" diff --git a/public/client/TracyCallstack.cpp b/public/common/TracyCallstack.cpp similarity index 99% rename from public/client/TracyCallstack.cpp rename to public/common/TracyCallstack.cpp index a801791de8..a4ff758e91 100644 --- a/public/client/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -7,10 +7,9 @@ #include "TracyCallstack.hpp" #include "TracyDebug.hpp" #include "TracyStringHelpers.hpp" -#include "../common/TracyAlloc.hpp" -#include "../common/TracySystem.hpp" -#include "../common/TracyDebugModulesHeaderFile.hpp" - +#include "TracyAlloc.hpp" +#include "TracySystem.hpp" +#include "TracyDebugModulesHeaderFile.hpp" #ifdef TRACY_HAS_CALLSTACK @@ -413,6 +412,9 @@ enum { MaxNameSize = 8*1024 }; int cb_num; CallstackEntry cb_data[MaxCbTrace]; +HANDLE symHandle = 0; +#pragma comment( lib, "dbghelp.lib" ) + extern "C" { typedef DWORD (__stdcall *t_SymAddrIncludeInlineTrace)( HANDLE hProcess, DWORD64 Address ); @@ -702,7 +704,7 @@ void DbgHelpInit(bool invadeProcess) DBGHELP_LOCK; #endif - assert(SymInitialize(symHandle, nullptr, false), "Failed to Init SymInitialize"); + assert(SymInitialize(symHandle, nullptr, false) && "Failed to Init SymInitialize"); //if (s_serverLocalResolve) // SymSetSearchPath(symHandle, R"(srv*C:\Users\Gabriel\AppData\Local\Temp\SymbolCache*https://msdl.microsoft.com/download/symbols)"); diff --git a/public/client/TracyCallstack.h b/public/common/TracyCallstack.h similarity index 100% rename from public/client/TracyCallstack.h rename to public/common/TracyCallstack.h diff --git a/public/client/TracyCallstack.hpp b/public/common/TracyCallstack.hpp similarity index 99% rename from public/client/TracyCallstack.hpp rename to public/common/TracyCallstack.hpp index 69d59328da..5fe240505f 100644 --- a/public/client/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -36,7 +36,7 @@ static tracy_force_inline void* Callstack( int32_t /*depth*/ ) { return nullptr; #include #include "../common/TracyAlloc.hpp" -#include "TracyFastVector.hpp" +#include "../common/TracyFastVector.hpp" namespace tracy { diff --git a/public/client/TracyDebug.hpp b/public/common/TracyDebug.hpp similarity index 100% rename from public/client/TracyDebug.hpp rename to public/common/TracyDebug.hpp diff --git a/public/client/TracyFastVector.hpp b/public/common/TracyFastVector.hpp similarity index 99% rename from public/client/TracyFastVector.hpp rename to public/common/TracyFastVector.hpp index 9df0466ba7..9520e73e3d 100644 --- a/public/client/TracyFastVector.hpp +++ b/public/common/TracyFastVector.hpp @@ -3,6 +3,7 @@ #include #include +#include #include "../common/TracyAlloc.hpp" #include "../common/TracyForceInline.hpp" diff --git a/public/client/TracyStringHelpers.hpp b/public/common/TracyStringHelpers.hpp similarity index 100% rename from public/client/TracyStringHelpers.hpp rename to public/common/TracyStringHelpers.hpp diff --git a/public/libbacktrace/elf.cpp b/public/libbacktrace/elf.cpp index ffe8d7024b..c1a438b72d 100644 --- a/public/libbacktrace/elf.cpp +++ b/public/libbacktrace/elf.cpp @@ -47,7 +47,7 @@ POSSIBILITY OF SUCH DAMAGE. */ #include "backtrace.hpp" #include "internal.hpp" -#include "../client/TracyFastVector.hpp" +#include "../common/TracyFastVector.hpp" #include "../common/TracyAlloc.hpp" #ifndef S_ISLNK diff --git a/public/tracy/TracyD3D11.hpp b/public/tracy/TracyD3D11.hpp index 810aa225f9..2eae21db68 100644 --- a/public/tracy/TracyD3D11.hpp +++ b/public/tracy/TracyD3D11.hpp @@ -38,7 +38,7 @@ using TracyD3D11Ctx = void*; #include "Tracy.hpp" #include "../client/TracyProfiler.hpp" -#include "../client/TracyCallstack.hpp" +#include "../common/TracyCallstack.hpp" #include "../common/TracyYield.hpp" #include diff --git a/public/tracy/TracyD3D12.hpp b/public/tracy/TracyD3D12.hpp index d36253d7cd..9f13e60765 100644 --- a/public/tracy/TracyD3D12.hpp +++ b/public/tracy/TracyD3D12.hpp @@ -34,7 +34,7 @@ using TracyD3D12Ctx = void*; #include "Tracy.hpp" #include "../client/TracyProfiler.hpp" -#include "../client/TracyCallstack.hpp" +#include "../common/TracyCallstack.hpp" #include #include diff --git a/public/tracy/TracyMetal.hmm b/public/tracy/TracyMetal.hmm index a4b4cb5216..98f909dbee 100644 --- a/public/tracy/TracyMetal.hmm +++ b/public/tracy/TracyMetal.hmm @@ -63,7 +63,7 @@ using TracyMetalCtx = void; #include "Tracy.hpp" #include "../client/TracyProfiler.hpp" -#include "../client/TracyCallstack.hpp" +#include "../common/TracyCallstack.hpp" #include "../common/TracyAlign.hpp" #include "../common/TracyAlloc.hpp" diff --git a/public/tracy/TracyOpenCL.hpp b/public/tracy/TracyOpenCL.hpp index ede5c4613b..33904b9746 100644 --- a/public/tracy/TracyOpenCL.hpp +++ b/public/tracy/TracyOpenCL.hpp @@ -40,7 +40,7 @@ using TracyCLCtx = void*; #include #include "Tracy.hpp" -#include "../client/TracyCallstack.hpp" +#include "../common/TracyCallstack.hpp" #include "../client/TracyProfiler.hpp" #include "../common/TracyAlloc.hpp" diff --git a/public/tracy/TracyOpenGL.hpp b/public/tracy/TracyOpenGL.hpp index 30abd4fd05..90a68ce264 100644 --- a/public/tracy/TracyOpenGL.hpp +++ b/public/tracy/TracyOpenGL.hpp @@ -37,7 +37,7 @@ class GpuCtxScope #include "Tracy.hpp" #include "../client/TracyProfiler.hpp" -#include "../client/TracyCallstack.hpp" +#include "../common/TracyCallstack.hpp" #include "../common/TracyAlign.hpp" #include "../common/TracyAlloc.hpp" diff --git a/public/tracy/TracyVulkan.hpp b/public/tracy/TracyVulkan.hpp index 72643188f8..107d9465da 100644 --- a/public/tracy/TracyVulkan.hpp +++ b/public/tracy/TracyVulkan.hpp @@ -40,7 +40,7 @@ using TracyVkCtx = void*; #include #include "Tracy.hpp" #include "../client/TracyProfiler.hpp" -#include "../client/TracyCallstack.hpp" +#include "../common/TracyCallstack.hpp" #include diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 13c83f9176..1af7d54663 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -26,7 +26,7 @@ #define ZDICT_STATIC_LINKING_ONLY #include -#include "../client/TracyCallstack.hpp" +#include "../common/TracyCallstack.hpp" #include "TracyDebugModulesHeaderFile.hpp" #include "../public/common/TracyProtocol.hpp" From e8e60d42d8d560f0d880b44a6abc3951f458baae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sat, 8 Mar 2025 21:26:03 +0100 Subject: [PATCH 05/40] Fix module info init and free. Clean InitCallstack --- public/client/TracyProfiler.cpp | 15 +- public/client/TracyProfiler.hpp | 2 +- public/common/TracyCallstack.cpp | 442 ++++++++++++++----------------- public/common/TracyCallstack.hpp | 2 +- server/TracyWorker.cpp | 16 +- 5 files changed, 213 insertions(+), 264 deletions(-) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 3840b7af97..ba9411a57e 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -1984,6 +1984,8 @@ void Profiler::Worker() } m_deferredLock.unlock(); #endif + // Send all known modules information. + SendModulesCaches(); // Main communications loop int keepAlive = 0; @@ -4065,7 +4067,7 @@ void Profiler::ReportTopology() # endif } -void Profiler::sendModulesCaches() +void Profiler::SendModulesCaches() { // We are retrieving modules information from the Tracy Profiler thread // But the Symbol Worker has ownership. Make sure it is not writing to the cache while we read it. @@ -4075,6 +4077,12 @@ void Profiler::sendModulesCaches() size_t moduleCount = -1; + const FastVector& kernelDrivers = GetKernelDriver(); + for (auto& it : kernelDrivers) + { + SendModuleInfo(it, &sendQueuBuffer); + } + const FastVector& cacheModule = GetModuleData(); // Sennd all moduleInfo for (auto& it : cacheModule) @@ -4083,11 +4091,6 @@ void Profiler::sendModulesCaches() } - const FastVector& kernelDrivers = GetKernelDriver(); - for (auto& it : kernelDrivers) - { - SendModuleInfo(it, &sendQueuBuffer); - } } diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index 96f751df69..3d6f799dec 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -920,7 +920,7 @@ class Profiler void CalibrateDelay(); void ReportTopology(); - void sendModulesCaches(); + void SendModulesCaches(); void WriteDebugFieldToPacket(uint8_t** ptr, int* currentPacketSize, const DegugModuleField& degugModuleField); void SendModuleInfo(const ModuleCacheEntry& moduleCacheEntry, std::vector* queuBuffer); diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index a4ff758e91..5ed6c1d25e 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -11,9 +11,18 @@ #include "TracySystem.hpp" #include "TracyDebugModulesHeaderFile.hpp" + + +# pragma optimize( "", off ) + + + + + + #ifdef TRACY_HAS_CALLSTACK -constexpr uint32_t ImageCacheAllocSize = 512; +constexpr uint32_t ImageCacheBaseCapacity = 512; #define CLIENT_SEND_IMAGES_INFO "CLIENT_SEND_IMAGES_INFO" #define SERVER_LOCAL_RESOLVE "SERVER_LOCAL_RESOLVE" @@ -95,6 +104,16 @@ namespace tracy return s_cacheMutex; } + void DestroyModuleCacheEntry(ModuleCacheEntry& entry) + { + tracy_free_fast(entry.path); + entry.path = nullptr; + tracy_free_fast(entry.name); + entry.name = nullptr; + tracy_free(entry.degugModuleField.debugData); + entry.degugModuleField.debugData = nullptr; + } + class ImageCache { public: @@ -116,11 +135,6 @@ namespace tracy return nullptr; } - ModuleCacheEntry* PushBack() - { - return m_modCache.push_next(); - } - void MapModuleData(const ModuleCacheEntry** moduleCacheEntries, size_t* moduleCount) { @@ -137,31 +151,16 @@ namespace tracy ModuleCacheEntry* CacheModuleWithDebugInfo(const ModuleCacheEntry& entry) { - ModuleCacheEntry* it = PushBack(); - *it = entry; - return it; + ModuleCacheEntry* newEntry = m_modCache.push_next(); + *newEntry = entry; + return newEntry; } void Clear() { - size_t mCacheS = 0; - - for (ModuleCacheEntry& cache : m_modCache) + for (ModuleCacheEntry& cacheEntry : m_modCache) { - if (cache.name != nullptr) - { - tracy_free(cache.name); - cache.name = nullptr; - } - - DegugModuleField& dbf = cache.degugModuleField; - - if (dbf.debugData != nullptr) - { - tracy_free(dbf.debugData); - dbf.debugData = nullptr; - } - mCacheS++; + DestroyModuleCacheEntry(cacheEntry); } m_modCache.clear(); } @@ -240,7 +239,6 @@ extern "C" const char* ___tracy_demangle( const char* mangled ) # define TRACY_USE_IMAGE_CACHE # include #endif -#include namespace tracy { @@ -412,7 +410,7 @@ enum { MaxNameSize = 8*1024 }; int cb_num; CallstackEntry cb_data[MaxCbTrace]; -HANDLE symHandle = 0; +HANDLE s_DbgHelpSymHandle = 0; #pragma comment( lib, "dbghelp.lib" ) extern "C" @@ -447,32 +445,15 @@ FastVector* s_krnlCache = nullptr; -class ImageCacheWindows : public ImageCache -{ -public: - - ImageCacheWindows(size_t moduleAllocationSize) : ImageCache(moduleAllocationSize) - { - - } - - ~ImageCacheWindows() = default; - -private: - -}; - -ImageCacheWindows* s_imageCacheWindows = nullptr; - - +ImageCache* s_imageCacheWindows = nullptr; uint64_t DbgHelpLoadSymbolsForModule(const char* imageName, uint64_t baseOfDll, uint32_t dllSize) { - auto d = SymLoadModuleEx(symHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0); + auto d = SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0); IMAGEHLP_MODULEW64 moduleInfo{}; moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); - if (TRUE == SymGetModuleInfoW64(symHandle, (uintptr_t)baseOfDll, &moduleInfo)) + if (TRUE == SymGetModuleInfoW64(s_DbgHelpSymHandle, (uintptr_t)baseOfDll, &moduleInfo)) { wprintf(L"- ImageName=%s\n", moduleInfo.ImageName); wprintf(L"- LoadedPdbName=%s\n", moduleInfo.LoadedPdbName); @@ -482,82 +463,6 @@ uint64_t DbgHelpLoadSymbolsForModule(const char* imageName, uint64_t baseOfDll, } - -ModuleCacheEntry* LoadSymbolsForModuleAndCache(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) -{ - - DbgHelpLoadSymbolsForModule(imageName, baseOfDll, dllSize); - - std::lock_guard mutexguard{ s_cacheMutex }; - - ModuleCacheEntry* cachedModuleEntry = s_imageCacheWindows->PushBack(); - cachedModuleEntry->start = baseOfDll; - cachedModuleEntry->end = baseOfDll + dllSize; - - // when doing offline symbol resolution, we must store the full path of the dll for the resolving to work - if (s_shouldResolveSymbolsOffline) - { - cachedModuleEntry->name = (char*)tracy_malloc_fast(imageNameLength + 1); - memcpy(cachedModuleEntry->name, imageName, imageNameLength); - cachedModuleEntry->name[imageNameLength] = '\0'; - } - else - { - FormatImageName(&cachedModuleEntry->name, imageName, imageNameLength); - } - - return cachedModuleEntry; -} - -void CacheKernelModule(const char* imageName, uint64_t baseOfDll, uint32_t dllSize, DebugFormat* debugFormat , uint8_t** debugData, uint32_t* debugSize) -{ - auto d = SymLoadModuleEx(symHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0); - - IMAGEHLP_MODULEW64 moduleInfo{}; - moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); - if (TRUE == SymGetModuleInfoW64(symHandle, (uintptr_t)baseOfDll, &moduleInfo)) - { - *debugFormat = DebugFormat::PdbDebugFormat; - - static constexpr int endofString = 1; - const uint32_t signatureLenght = static_cast(wcslen(reinterpret_cast(moduleInfo.CVData))); - - const uint32_t debugFormatSize = sizeof(WindowsDebugData) + signatureLenght + endofString; - - *debugData = (uint8_t*)tracy_malloc(debugFormatSize); - *debugSize = debugFormatSize; - WindowsDebugData* ptrToWindowsDebugData = reinterpret_cast(*debugData); - ptrToWindowsDebugData->majorVersion = 0; - ptrToWindowsDebugData->minorVersion = 0; - ptrToWindowsDebugData->exeDataTimeStamp = moduleInfo.TimeDateStamp; - - TracyPdbInfo* pdbInfo = &ptrToWindowsDebugData->cvInfo; - - pdbInfo->Age = moduleInfo.PdbAge; - pdbInfo->CvSignature = moduleInfo.CVSig; - - static_assert(sizeof(pdbInfo->Signature) == sizeof(moduleInfo.PdbSig70), "GUID size must match"); - memcpy(&pdbInfo->Signature, &moduleInfo.PdbSig70, sizeof(moduleInfo.PdbSig70)); - - const GUID* guidd = reinterpret_cast(&pdbInfo->Signature); - - using convert_type = std::codecvt_utf8; - - std::wstring s = moduleInfo.CVData; - std::wstring_convert converter; - std::string converted_str = converter.to_bytes(s); - - char* outSingature = (char*)memcpy((*debugData) + sizeof(WindowsDebugData) - , converted_str.data(), signatureLenght + endofString); - - const CV_INFO_PDB70* pData = reinterpret_cast(pdbInfo); - - SymUnloadModule(symHandle, baseOfDll); - } - -} - - bool GetModulePDBData(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize) { if (!s_clientSendImageInfo) @@ -588,9 +493,11 @@ bool GetModulePDBData(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** de if (debugDirectory[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW) continue; - CV_INFO_PDB70* pData = - (CV_INFO_PDB70*)(uintptr_t(BaseAddress) + (MappedAsImage ? debugDirectory[i].AddressOfRawData - : debugDirectory[i].PointerToRawData)); + CV_INFO_PDB70* pData = (CV_INFO_PDB70*)(uintptr_t(BaseAddress) + + (MappedAsImage + ? debugDirectory[i].AddressOfRawData + : debugDirectory[i].PointerToRawData) + ); if (pData->CvSignature != CV_SIGNATURE_RSDS) continue; @@ -621,6 +528,87 @@ bool GetModulePDBData(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** de return false; } +ModuleCacheEntry* CacheModuleInfo(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) +{ + + ModuleCacheEntry moduleEntry = {}; + moduleEntry.start = baseOfDll; + moduleEntry.end = baseOfDll + dllSize; + moduleEntry.path = CopyStringFast(imageName, imageNameLength); + FormatImageName(&moduleEntry.name, imageName, imageNameLength); + + DebugFormat debugFormat = DebugFormat::NoDebugFormat; + uint8_t* debugData = nullptr; + uint32_t debugDataSize = 0; + + if (GetModulePDBData(moduleEntry.start, &debugFormat, &debugData, &debugDataSize)) + { + DegugModuleField& dmf = moduleEntry.degugModuleField; + + dmf.debugFormat = debugFormat; + dmf.debugData = debugData; + dmf.debugDataSize = debugDataSize; + } + + std::lock_guard mutexguard{ s_cacheMutex }; + return s_imageCacheWindows->CacheModuleWithDebugInfo(moduleEntry); +} + +ModuleCacheEntry* LoadSymbolsForModuleAndCache(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) +{ + DbgHelpLoadSymbolsForModule(imageName, baseOfDll, dllSize); + + return CacheModuleInfo(imageName, imageNameLength, baseOfDll, dllSize); +} + +void GetModuleInfoFromDbgHelp(const char* imageName, ModuleCacheEntry* moduleEntry) +{ + IMAGEHLP_MODULE64 moduleInfo{}; + moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); + if (TRUE == SymGetModuleInfo64(s_DbgHelpSymHandle, moduleEntry->start, &moduleInfo)) + { + if (moduleInfo.SymType == SymDeferred) // If symbol loading was deferred, force load it so that we can retrieve the debug informations + { + DWORD prevOptions = SymGetOptions(); + SymSetOptions(prevOptions & (~SYMOPT_DEFERRED_LOADS)); + DWORD64 loadedAddr = SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, imageName, nullptr, moduleEntry->start, moduleEntry->end ? (moduleEntry->end - moduleEntry->start) : 0, nullptr, 0); + SymSetOptions(prevOptions); + if(!SymGetModuleInfo64(s_DbgHelpSymHandle, moduleEntry->start, &moduleInfo)) + { + return; + } + } + + if (moduleInfo.CVSig != CV_SIGNATURE_RSDS) // Do we have a pdb ? + return; + + DegugModuleField& debugInfo = moduleEntry->degugModuleField; + debugInfo.debugFormat = DebugFormat::PdbDebugFormat; + + const uint32_t pdbFileNameLen = static_cast(strlen(moduleInfo.CVData)); + debugInfo.debugDataSize = sizeof(WindowsDebugData) + (pdbFileNameLen + 1); + debugInfo.debugData = (uint8_t*)tracy_malloc(debugInfo.debugDataSize); + WindowsDebugData* ptrToWindowsDebugData = reinterpret_cast(debugInfo.debugData); + ptrToWindowsDebugData->majorVersion = 0; + ptrToWindowsDebugData->minorVersion = 0; + ptrToWindowsDebugData->exeDataTimeStamp = moduleInfo.TimeDateStamp; + + TracyPdbInfo* pdbInfo = &ptrToWindowsDebugData->cvInfo; + + pdbInfo->Age = moduleInfo.PdbAge; + pdbInfo->CvSignature = moduleInfo.CVSig; + + static_assert(sizeof(pdbInfo->Signature) == sizeof(moduleInfo.PdbSig70), "GUID size must match"); + memcpy(&pdbInfo->Signature, &moduleInfo.PdbSig70, sizeof(moduleInfo.PdbSig70)); + + const GUID* guidd = reinterpret_cast(&pdbInfo->Signature); + + char* pdbFileName = (char*)debugInfo.debugData + sizeof(WindowsDebugData); + memcpy(pdbFileName, moduleInfo.CVData, pdbFileNameLen + 1); + } +} + + ModuleNameAndBaseAddress OnFailedFindAddress(uint64_t address) { InitRpmalloc(); @@ -692,31 +680,40 @@ void InitCallstackCritical() ___tracy_RtlWalkFrameChain = (___tracy_t_RtlWalkFrameChain)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlWalkFrameChain"); } -void DbgHelpInit(bool invadeProcess) +void DbgHelpInit(HANDLE symHandle, bool invadeProcess) { - _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"); + assert(s_DbgHelpSymHandle == 0); + s_DbgHelpSymHandle = symHandle; + + HMODULE DbgHelpHdl = GetModuleHandleA("dbghelp.dll"); + if (!DbgHelpHdl) { + TracyDebug("Couldn't load DbgHelp.dll\n"); + return; + } + + _SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress(DbgHelpHdl, "SymAddrIncludeInlineTrace"); + _SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress(DbgHelpHdl, "SymQueryInlineTrace"); + _SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress(DbgHelpHdl, "SymFromInlineContext"); + _SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress(DbgHelpHdl, "SymGetLineFromInlineContext"); #ifdef TRACY_DBGHELP_LOCK DBGHELP_INIT; DBGHELP_LOCK; #endif - assert(SymInitialize(symHandle, nullptr, false) && "Failed to Init SymInitialize"); - - //if (s_serverLocalResolve) - // SymSetSearchPath(symHandle, R"(srv*C:\Users\Gabriel\AppData\Local\Temp\SymbolCache*https://msdl.microsoft.com/download/symbols)"); - - SymSetOptions(SYMOPT_LOAD_LINES + SymSetOptions( + SYMOPT_LOAD_LINES | SYMOPT_UNDNAME // TODO: check if tracy doesn't rely on this to find decorated names in the dissassembler - /* | SYMOPT_DEFERRED_LOADS*/ + | SYMOPT_DEFERRED_LOADS #ifndef NDEBUG | SYMOPT_DEBUG #endif ); + if (!SymInitialize(symHandle, nullptr, false)) + { + TracyDebug("Failed to initalize DbgHelp %x\n", GetLastError()); + } #ifdef TRACY_DBGHELP_LOCK DBGHELP_UNLOCK; @@ -724,7 +721,7 @@ void DbgHelpInit(bool invadeProcess) } -void CacheProcessDrivers() +static void CacheProcessDrivers() { DWORD needed; LPVOID dev[4096]; @@ -767,17 +764,16 @@ void CacheProcessDrivers() path = full; } - if (!s_clientSendImageInfo) - DbgHelpLoadSymbolsForModule(path, (DWORD64)dev[i], 0); - else - CacheKernelModule(path, kernelDriver->start, 0, &kernelDriver->degugModuleField.debugFormat, &kernelDriver->degugModuleField.debugData, - &kernelDriver->degugModuleField.debugDataSize); - - const auto psz = strlen( path ); - auto pptr = (char*)tracy_malloc_fast( psz+1 ); - memcpy( pptr, path, psz ); - pptr[psz] = '\0'; - kernelDriver->path = pptr; + kernelDriver->path = CopyStringFast(path); + + if (SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0)) + { + GetModuleInfoFromDbgHelp(path, kernelDriver); + // We no longer need it if we resolve symbols offline, unload it. + if (s_shouldResolveSymbolsOffline) { + SymUnloadModule64(s_DbgHelpSymHandle, (DWORD64)dev[i]); + } + } } assert(kernelDriver->end == 0 && "kernel end should be zero"); @@ -788,7 +784,7 @@ void CacheProcessDrivers() } } -void CacheProcessModule() +static void CacheProcessModules() { DWORD needed; HANDLE proc = GetCurrentProcess(); @@ -807,6 +803,7 @@ void CacheProcessModule() { // This may be a new module loaded since our call to SymInitialize. // Just in case, force DbgHelp to load its pdb ! + if (!s_shouldResolveSymbolsOffline) { auto moduleCache = LoadSymbolsForModuleAndCache(name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage); @@ -816,27 +813,6 @@ void CacheProcessModule() { uint64_t baseAdd = (DWORD64)info.lpBaseOfDll; - ModuleCacheEntry* cachedModule = s_imageCacheWindows->PushBack(); - cachedModule->start = (DWORD64)info.lpBaseOfDll; - cachedModule->end = baseAdd + info.SizeOfImage; - cachedModule->path = (char*)tracy_malloc(nameLength); - memcpy(cachedModule->path, name, nameLength); - - FormatImageName(&cachedModule->name, name, nameLength); - - DebugFormat debugFormat = DebugFormat::NoDebugFormat; - uint8_t* debugData = nullptr; - uint32_t debugDataSize = 0; - - if (GetModulePDBData(cachedModule->start, &debugFormat, &debugData, &debugDataSize)) - { - DegugModuleField& dmf = cachedModule->degugModuleField; - - dmf.debugFormat = debugFormat; - dmf.debugData = debugData; - dmf.debugDataSize = debugDataSize; - } - } } @@ -845,40 +821,6 @@ void CacheProcessModule() } } - -void CacheDriverAndModuleData() -{ - symHandle = GetCurrentProcess(); - DbgHelpInit(true); - - // 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\n"); - } - - std::lock_guard mutexguard{ s_cacheMutex }; - - s_imageCacheWindows = (ImageCacheWindows*)tracy_malloc(sizeof(ImageCacheWindows)); - new(s_imageCacheWindows) ImageCacheWindows(ImageCacheAllocSize); - - s_krnlCache = (FastVector*)tracy_malloc(sizeof(FastVector)); - new(s_krnlCache) FastVector(ImageCacheAllocSize); - - - if (initTimeModuleLoad) - { - CacheProcessDrivers(); - CacheProcessModule(); - } -} - - void InitCallstack() { s_serverLocalResolve = IsEnv(SERVER_LOCAL_RESOLVE); @@ -893,37 +835,45 @@ void InitCallstack() TracyDebug("TRACY: enabling offline symbol resolving!\n"); } - if (!s_shouldResolveSymbolsOffline && !s_clientSendImageInfo && !s_serverLocalResolve) - { - CacheDriverAndModuleData(); - return; - } +#ifdef TRACY_ENABLE // Client or self-profiling server + // Use GetCurrentProcess() as this is used by default in most apps, but we should probably be using a fake handle to avoid collisions? + DbgHelpInit(GetCurrentProcess(), true /*Invade process, even though it should be unnecessary since we'll preload all modules and drivers*/); +#else + DbgHelpInit((HANDLE)42/*This is our fake DbgHelp Handle*/, false /* Don't invade the process, we're going to resolve symbols for another one*/); +#endif + s_imageCacheWindows = (ImageCache*)tracy_malloc(sizeof(ImageCache)); + new(s_imageCacheWindows) ImageCache(ImageCacheBaseCapacity); + s_krnlCache = (FastVector*)tracy_malloc(sizeof(FastVector)); + new(s_krnlCache) FastVector(ImageCacheBaseCapacity); - // the client will send all of his moduleData - if (s_clientSendImageInfo) +#ifdef TRACY_ENABLE // Client or self-profiling server +#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) { - CacheDriverAndModuleData(); + TracyDebug("TRACY: skipping init time dbghelper module load\n"); } + + std::lock_guard mutexguard{ s_cacheMutex }; - if (s_serverLocalResolve) + if (initTimeModuleLoad) { - // Init debg and allocate moduleCache - // waiting for client sending his module datas - symHandle = (HANDLE)42; - static constexpr bool shoulInvadeProcess = false; - - DbgHelpInit(shoulInvadeProcess); - s_imageCacheWindows = (ImageCacheWindows*)tracy_malloc(sizeof(ImageCacheWindows)); - new(s_imageCacheWindows) ImageCacheWindows(ImageCacheAllocSize); - s_krnlCache = (FastVector*)tracy_malloc(sizeof(FastVector)); - new(s_krnlCache) FastVector(ImageCacheAllocSize); + CacheProcessDrivers(); + CacheProcessModules(); } - // TO DO FIX IT MAY BE BROKEN #ifdef TRACY_DBGHELP_LOCK DBGHELP_UNLOCK; #endif +#endif } void FreeKernelCache() @@ -935,23 +885,10 @@ void FreeKernelCache() for (auto& it : *s_krnlCache) { - - if (it.name != nullptr) - { - tracy_free((void*)it.name); - it.name = nullptr; - } - - if (it.path != nullptr) - { - tracy_free((void*)it.path); - it.path = nullptr; - } - + DestroyModuleCacheEntry(it); } tracy_free((void*)s_krnlCache); s_krnlCache = nullptr; - } void EndCallstack() @@ -960,10 +897,15 @@ void EndCallstack() if (s_imageCacheWindows != nullptr) { - s_imageCacheWindows->~ImageCacheWindows(); + s_imageCacheWindows->~ImageCache(); tracy_free(s_imageCacheWindows); s_imageCacheWindows = nullptr; } + if (s_DbgHelpSymHandle) + { + SymCleanup(s_DbgHelpSymHandle); + s_DbgHelpSymHandle = 0; + } } const char* DecodeCallstackPtrFast(uint64_t ptr) @@ -1054,14 +996,14 @@ bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, const CV_INFO_PDB70* dummie = reinterpret_cast(&windowsDebugData->cvInfo); - DWORD64 loaddedModule = SymLoadModuleEx(symHandle, NULL, moduleName, NULL, baseAddress, + DWORD64 loaddedModule = SymLoadModuleEx(s_DbgHelpSymHandle, NULL, moduleName, NULL, baseAddress, dllSize, &module_load_info, 0); IMAGEHLP_MODULEW64 modulInfoDebug{}; modulInfoDebug.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); - if (SymGetModuleInfoW64(symHandle, loaddedModule, &modulInfoDebug) == TRUE) + if (SymGetModuleInfoW64(s_DbgHelpSymHandle, loaddedModule, &modulInfoDebug) == TRUE) { if (modulInfoDebug.SymType != SymNone) { @@ -1075,11 +1017,15 @@ bool IsKernelAddress(uint64_t addr) { return (addr >> 63) != 0; } -// Called from the profiler only, we received data from the client. -void CacheModuleAndLoadExternal(const ModuleCacheEntry& moduleCacheEntry) +// Called from the profiler (server) only, we received data from the client. +void CacheModuleAndLoadExternal(ModuleCacheEntry& moduleCacheEntry) { + if (!s_serverLocalResolve) + { + DestroyModuleCacheEntry(moduleCacheEntry); return; + } #if 1 // windows if (IsKernelAddress(moduleCacheEntry.start)) @@ -1146,7 +1092,7 @@ CallstackSymbolData DecodeSymbolAddress(uint64_t ptr) #ifdef TRACY_DBGHELP_LOCK DBGHELP_LOCK; #endif - const auto res = SymGetLineFromAddr64(symHandle, ptr, &displacement, &line); + const auto res = SymGetLineFromAddr64(s_DbgHelpSymHandle, ptr, &displacement, &line); if (res == 0 || line.LineNumber >= 0xF00000) { sym.file = "[unknown]"; @@ -1209,7 +1155,7 @@ CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _d } int write; - const auto proc = symHandle; + const auto proc = s_DbgHelpSymHandle; #if !defined TRACY_NO_CALLSTACK_INLINES BOOL doInline = FALSE; diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index 5fe240505f..888ab3e05a 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -111,7 +111,7 @@ void FindModuleFromAddr(uint64_t addr, const ModuleCacheEntry** outModule); void FindKernelDriverFromAddr(uint64_t addr, const ModuleCacheEntry** outDrive); -void CacheModuleAndLoadExternal(const ModuleCacheEntry& moduleCacheEntry); +void CacheModuleAndLoadExternal(ModuleCacheEntry& moduleCacheEntry); void CacheModuleKernelAndLoadExternal(const ModuleCacheEntry& kernelDriver); const FastVector& GetModuleData(); const FastVector& GetKernelDriver(); diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 1af7d54663..8d8f7b4800 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -1670,14 +1670,14 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow f.Read(debugField->debugData, debugField->debugDataSize); }; - auto DeserializeMallocedString = [&](char** s) + auto DeserializeMallocedFastString = [&](char** s) { int sLenght = -1; f.Read(sLenght); if (sLenght == -1) return; - *s = (char*)tracy_malloc(sLenght); + *s = (char*)tracy_malloc_fast(sLenght); f.Read(*s, sLenght); @@ -1693,8 +1693,8 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow ModuleCacheEntry moduleEntry; f.Read(moduleEntry.start); f.Read(moduleEntry.end); - DeserializeMallocedString(&moduleEntry.name); - DeserializeMallocedString(&moduleEntry.path); + DeserializeMallocedFastString(&moduleEntry.name); + DeserializeMallocedFastString(&moduleEntry.path); DeserializeDebugField(&moduleEntry.degugModuleField); @@ -1712,11 +1712,11 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow f.Read(driver.start); driver.end = 0; char* name = nullptr; - DeserializeMallocedString(&name); + DeserializeMallocedFastString(&name); driver.name = name; char* path = nullptr; - DeserializeMallocedString(&path); + DeserializeMallocedFastString(&path); driver.path = path; DeserializeDebugField(&driver.degugModuleField); @@ -8922,7 +8922,7 @@ void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) uint32_t moduleNameSize = MemRead( ptrToData ); ptrToData += sizeof(moduleNameSize); - char* moduleName = (char*)tracy_malloc(moduleNameSize); + char* moduleName = (char*)tracy_malloc_fast(moduleNameSize); memcpy(moduleName, ptrToData, moduleNameSize); ptrToData += moduleNameSize; StringLocation moduleNameStringLocation = StoreString(moduleName); @@ -8931,7 +8931,7 @@ void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) uint32_t modulePathSize = MemRead(ptrToData); ptrToData += sizeof(modulePathSize); - char* modulePath = (char*)tracy_malloc(modulePathSize); + char* modulePath = (char*)tracy_malloc_fast(modulePathSize); memcpy( modulePath, ptrToData, modulePathSize ); ptrToData += modulePathSize; StringLocation modulePathStringLocation = StoreString(modulePath); From ce22a0e3b007afcf8375d1e839ac888497695650 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sun, 9 Mar 2025 00:50:10 +0100 Subject: [PATCH 06/40] Remove s_clientSendImageInfo for now and make s_serverLocalResolve true by default --- public/common/TracyCallstack.cpp | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 5ed6c1d25e..fc9b822097 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -24,10 +24,6 @@ constexpr uint32_t ImageCacheBaseCapacity = 512; -#define CLIENT_SEND_IMAGES_INFO "CLIENT_SEND_IMAGES_INFO" -#define SERVER_LOCAL_RESOLVE "SERVER_LOCAL_RESOLVE" - - #if TRACY_HAS_CALLSTACK == 1 # ifndef NOMINMAX # define NOMINMAX @@ -80,8 +76,7 @@ namespace tracy #else static bool s_shouldResolveSymbolsOffline = false; - static bool s_clientSendImageInfo = false; - static bool s_serverLocalResolve = false; // Should the profiler try to resolve symbols before querying the client for symbols. + bool s_serverLocalResolve = true; // Should the profiler try to resolve symbols before querying the client for symbols. inline bool IsEnv(const char* environementVariableName) { @@ -138,7 +133,7 @@ namespace tracy void MapModuleData(const ModuleCacheEntry** moduleCacheEntries, size_t* moduleCount) { - if (!s_clientSendImageInfo && m_modCache.empty()) + if (m_modCache.empty()) { *moduleCacheEntries = nullptr; *moduleCount = 0; @@ -465,9 +460,6 @@ uint64_t DbgHelpLoadSymbolsForModule(const char* imageName, uint64_t baseOfDll, bool GetModulePDBData(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize) { - if (!s_clientSendImageInfo) - return false; - static constexpr bool MappedAsImage = true; PVOID BaseAddress = (void*)baseOfDll; @@ -823,8 +815,7 @@ static void CacheProcessModules() void InitCallstack() { - s_serverLocalResolve = IsEnv(SERVER_LOCAL_RESOLVE); - s_clientSendImageInfo = IsEnv(CLIENT_SEND_IMAGES_INFO); + s_serverLocalResolve = IsEnv("SERVER_LOCAL_RESOLVE"); #ifndef TRACY_SYMBOL_OFFLINE_RESOLVE From 1444ad3c2ad9ba4e00b64a20b2af9be116d420e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sun, 9 Mar 2025 00:53:44 +0100 Subject: [PATCH 07/40] IsEnv now supports default value --- public/common/TracyCallstack.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index fc9b822097..3bea0c1b13 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -78,11 +78,11 @@ namespace tracy bool s_serverLocalResolve = true; // Should the profiler try to resolve symbols before querying the client for symbols. - inline bool IsEnv(const char* environementVariableName) + inline bool IsEnv(const char* environementVariableName, bool defaultValue) { const char* v = GetEnvVar(environementVariableName); - - return (v && v[0] == '1'); + if (v) return v[0] == '1'; + else return defaultValue; } struct ModuleNameAndBaseAddress @@ -393,7 +393,7 @@ void FormatImageName(char** moduleCacheName, const char* imageName, uint32_t ima bool ShouldResolveSymbolsOffline() { - return IsEnv("TRACY_SYMBOL_OFFLINE_RESOLVE"); + return IsEnv("TRACY_SYMBOL_OFFLINE_RESOLVE", false); } #endif // #ifdef TRACY_SYMBOL_OFFLINE_RESOLVE @@ -815,7 +815,7 @@ static void CacheProcessModules() void InitCallstack() { - s_serverLocalResolve = IsEnv("SERVER_LOCAL_RESOLVE"); + s_serverLocalResolve = IsEnv("SERVER_LOCAL_RESOLVE", true); #ifndef TRACY_SYMBOL_OFFLINE_RESOLVE From 2a74e0c63ea93f1cb95c843fc142d6b99c5c4384 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sun, 9 Mar 2025 00:57:13 +0100 Subject: [PATCH 08/40] Now fallback correctly when symbol is not found --- public/common/TracyCallstack.cpp | 97 +++++++++++++++++++------------- server/TracyWorker.cpp | 78 +++++++++++++------------ server/TracyWorker.hpp | 1 - 3 files changed, 99 insertions(+), 77 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 3bea0c1b13..ab9539e5cb 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -458,7 +458,7 @@ uint64_t DbgHelpLoadSymbolsForModule(const char* imageName, uint64_t baseOfDll, } -bool GetModulePDBData(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize) +bool GetModuleInfoFromPEHeaders(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize) { static constexpr bool MappedAsImage = true; @@ -520,39 +520,6 @@ bool GetModulePDBData(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** de return false; } -ModuleCacheEntry* CacheModuleInfo(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) -{ - - ModuleCacheEntry moduleEntry = {}; - moduleEntry.start = baseOfDll; - moduleEntry.end = baseOfDll + dllSize; - moduleEntry.path = CopyStringFast(imageName, imageNameLength); - FormatImageName(&moduleEntry.name, imageName, imageNameLength); - - DebugFormat debugFormat = DebugFormat::NoDebugFormat; - uint8_t* debugData = nullptr; - uint32_t debugDataSize = 0; - - if (GetModulePDBData(moduleEntry.start, &debugFormat, &debugData, &debugDataSize)) - { - DegugModuleField& dmf = moduleEntry.degugModuleField; - - dmf.debugFormat = debugFormat; - dmf.debugData = debugData; - dmf.debugDataSize = debugDataSize; - } - - std::lock_guard mutexguard{ s_cacheMutex }; - return s_imageCacheWindows->CacheModuleWithDebugInfo(moduleEntry); -} - -ModuleCacheEntry* LoadSymbolsForModuleAndCache(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) -{ - DbgHelpLoadSymbolsForModule(imageName, baseOfDll, dllSize); - - return CacheModuleInfo(imageName, imageNameLength, baseOfDll, dllSize); -} - void GetModuleInfoFromDbgHelp(const char* imageName, ModuleCacheEntry* moduleEntry) { IMAGEHLP_MODULE64 moduleInfo{}; @@ -600,6 +567,38 @@ void GetModuleInfoFromDbgHelp(const char* imageName, ModuleCacheEntry* moduleEnt } } +ModuleCacheEntry* CacheModuleInfo(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) +{ + + ModuleCacheEntry moduleEntry = {}; + moduleEntry.start = baseOfDll; + moduleEntry.end = baseOfDll + dllSize; + moduleEntry.path = CopyStringFast(imageName, imageNameLength); + FormatImageName(&moduleEntry.name, imageName, imageNameLength); + + DebugFormat debugFormat = DebugFormat::NoDebugFormat; + uint8_t* debugData = nullptr; + uint32_t debugDataSize = 0; + + if (GetModuleInfoFromPEHeaders(moduleEntry.start, &debugFormat, &debugData, &debugDataSize)) + { + DegugModuleField& dmf = moduleEntry.degugModuleField; + + dmf.debugFormat = debugFormat; + dmf.debugData = debugData; + dmf.debugDataSize = debugDataSize; + } + + std::lock_guard mutexguard{ s_cacheMutex }; + return s_imageCacheWindows->CacheModuleWithDebugInfo(moduleEntry); +} + +ModuleCacheEntry* LoadSymbolsForModuleAndCache(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) +{ + DbgHelpLoadSymbolsForModule(imageName, baseOfDll, dllSize); + + return CacheModuleInfo(imageName, imageNameLength, baseOfDll, dllSize); +} ModuleNameAndBaseAddress OnFailedFindAddress(uint64_t address) { @@ -760,6 +759,7 @@ static void CacheProcessDrivers() if (SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0)) { + // Kernel drivers PE headers are not accessible from userland, use DbgHelp to retrieve debug info. GetModuleInfoFromDbgHelp(path, kernelDriver); // We no longer need it if we resolve symbols offline, unload it. if (s_shouldResolveSymbolsOffline) { @@ -990,13 +990,14 @@ bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, DWORD64 loaddedModule = SymLoadModuleEx(s_DbgHelpSymHandle, NULL, moduleName, NULL, baseAddress, dllSize, &module_load_info, 0); - IMAGEHLP_MODULEW64 modulInfoDebug{}; + IMAGEHLP_MODULEW64 moduleInfoDebug{}; - modulInfoDebug.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); + moduleInfoDebug.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); if (SymGetModuleInfoW64(s_DbgHelpSymHandle, loaddedModule, &modulInfoDebug) == TRUE) { - if (modulInfoDebug.SymType != SymNone) + // Consider deferred to be failing too as it may fail later. We might want to handle failure in the UI. + if( moduleInfoDebug.SymType != SymNone && moduleInfoDebug.SymType != SymDeferred ) { return true; } @@ -1033,13 +1034,27 @@ void CacheModuleAndLoadExternal(ModuleCacheEntry& moduleCacheEntry) bool hasSymbolInfo = false; if (moduleCacheEntry.degugModuleField.debugFormat == DebugFormat::PdbDebugFormat) { - hasSymbolInfo = LoadFromPdb(moduleCacheEntry.name, moduleCacheEntry.start, moduleCacheEntry.end - moduleCacheEntry.start, + char* nameFixed = nullptr; + if (IsKernelAddress(moduleCacheEntry.start)) + { + const size_t nameLen = strlen(moduleCacheEntry.name); + if (nameLen > 3 && moduleCacheEntry.name[0] == '<' && moduleCacheEntry.name[nameLen - 1] == '>') + { + char* kernelName = CopyStringFast(moduleCacheEntry.name + 1); + kernelName[nameLen - 2] = 0; // replace > + nameFixed = kernelName; + } + } + hasSymbolInfo = LoadFromPdb(nameFixed ? nameFixed : moduleCacheEntry.name, moduleCacheEntry.start, moduleCacheEntry.end - moduleCacheEntry.start, moduleCacheEntry.degugModuleField.debugFormat, moduleCacheEntry.degugModuleField.debugData, moduleCacheEntry.degugModuleField.debugDataSize); + + if (nameFixed) tracy_free_fast(nameFixed); } if (!hasSymbolInfo) { // TODO: load from path only if we can check we got the correct binary (check timestamp / guid ?) + // That would however require to disable deferred symbol loading or use FindExecutableImageEx //DbgHelpLoadSymbolsForModule(moduleCacheEntry.path, moduleCacheEntry.start, moduleCacheEntry.end - moduleCacheEntry.start); } @@ -1179,7 +1194,11 @@ CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _d const auto symValid = SymFromAddr(proc, ptr, nullptr, si) != 0; - if (!symValid) + if (symValid) + { + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; + } + else { *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::SymbolMissing; } diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 8d8f7b4800..b82efaef06 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -4086,56 +4086,60 @@ uint64_t Worker::GetCanonicalPointer( const CallstackFrameId& id ) const return ( id.idx & 0x3FFFFFFFFFFFFFFF ) | ( ( id.idx & 0x3000000000000000 ) << 2 ); } +// TODO: handle properly +extern bool s_serverLocalResolve; + void Worker::TryResolveCallStackIfNeeded(CallstackFrameId frameId) { + // If we already have the information if (m_data.callstackFrameMap.count(frameId) != 0) { return; } - m_pendingCallstackFrames++; uint64_t symbolAddress = GetCanonicalPointer(frameId); - if (!m_resolveSymbolLocally) - { - Query(ServerQueryCallstackFrame, symbolAddress); - return; - } - - - DecodeCallStackPtrStatus status; - CallstackEntryData outCallStack = DecodeCallstackPtr(symbolAddress, &status); - - if (status != DecodeCallStackPtrStatus::Success) + + DecodeCallStackPtrStatus status = DecodeCallStackPtrStatus::Count; + if (s_serverLocalResolve) { - //Query(ServerQueryModuleUpdate, symbolAddress, 0); - } + // TODO: offload to a worker thread + CallstackEntryData outCallStack = DecodeCallstackPtr(symbolAddress, &status); + if (status == DecodeCallStackPtrStatus::Success) + { + const uint32_t imageNameIdx = StoreString(outCallStack.imageName).idx; + assert(outCallStack.size <= 255); + const QueueCallstackFrameSize queueCallstackFrameSize = + { + .ptr = symbolAddress, + .size = outCallStack.size, + }; + m_pendingCallstackFrames++; + ProcessCallstackFrameSize(queueCallstackFrameSize, imageNameIdx); - const uint32_t imageNameIdx = StoreString(outCallStack.imageName).idx; - assert(outCallStack.size <= 255); - const QueueCallstackFrameSize queueCallstackFrameSize = - { - .ptr = symbolAddress, - .size = outCallStack.size, - }; + // we know that for the best we have inline information + for (size_t i = 0; i < queueCallstackFrameSize.size; i++) + { + const auto& frame = outCallStack.data[i]; + ProcessCallstackFrame(frame.line, frame.symAddr, frame.symLen, + StoreString(frame.name).idx, StoreString(frame.file).idx, true); - ProcessCallstackFrameSize(queueCallstackFrameSize, imageNameIdx); + // We copied it, now free the content + tracy_free_fast((void*)frame.name); + tracy_free_fast((void*)frame.file); + } - // we know that for the best we have inline information - for (size_t i = 0; i < queueCallstackFrameSize.size; i++) + assert(m_pendingCallstackSubframes == 0); + } + } + // Fallback to asking the client. + // TODO: Only do this if client does not have TRACY_SYMBOL_OFFLINE_RESOLVE + if (status != DecodeCallStackPtrStatus::Success) { - const auto& frame = outCallStack.data[i]; - ProcessCallstackFrame(frame.line, frame.symAddr, frame.symLen, - StoreString(frame.name).idx, StoreString(frame.file).idx, true); - - // We copied it, now free the content - tracy_free_fast((void*)frame.name); - tracy_free_fast((void*)frame.file); + m_pendingCallstackFrames++; + Query(ServerQueryCallstackFrame, symbolAddress); + return; } - - assert(m_pendingCallstackSubframes == 0); - - } void Worker::AddCallstackPayload( const char* _data, size_t _sz ) @@ -8922,20 +8926,20 @@ void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) uint32_t moduleNameSize = MemRead( ptrToData ); ptrToData += sizeof(moduleNameSize); + assert((char)(ptrToData[moduleNameSize - 1]) == '\0' && "missing end of string"); char* moduleName = (char*)tracy_malloc_fast(moduleNameSize); memcpy(moduleName, ptrToData, moduleNameSize); ptrToData += moduleNameSize; StringLocation moduleNameStringLocation = StoreString(moduleName); - assert((char)(moduleName[moduleNameSize - 1]) == '\0' && "missing end of string"); uint32_t modulePathSize = MemRead(ptrToData); ptrToData += sizeof(modulePathSize); + assert((char)(ptrToData[modulePathSize - 1]) == '\0' && "missing end of string"); char* modulePath = (char*)tracy_malloc_fast(modulePathSize); memcpy( modulePath, ptrToData, modulePathSize ); ptrToData += modulePathSize; StringLocation modulePathStringLocation = StoreString(modulePath); - assert((char)(modulePath[modulePathSize - 1]) == '\0' && "missing end of string"); DebugFormat debugFormat = MemRead( ptrToData ); diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index e35a0abb36..7233b4abcd 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -1029,7 +1029,6 @@ class Worker bool m_identifySamples = false; bool m_inconsistentSamples; bool m_allowStringModification = false; - bool m_resolveSymbolLocally = true; short_ptr m_gpuCtxMap[256]; uint32_t m_pendingCallstackId = 0; From 901e826fe0c166fbee1a45a5dfb957fe84d8ba42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sun, 9 Mar 2025 02:06:05 +0100 Subject: [PATCH 09/40] Unify image caches across platforms --- public/TracyClient.cpp | 19 - public/client/TracyProfiler.cpp | 78 ++-- public/client/TracyProfiler.hpp | 6 +- public/common/TracyCallstack.cpp | 655 ++++++++++++++----------------- public/common/TracyCallstack.h | 4 + public/common/TracyCallstack.hpp | 13 +- public/common/TracyQueue.hpp | 6 +- server/TracyWorker.cpp | 53 ++- server/TracyWorker.hpp | 2 +- 9 files changed, 367 insertions(+), 469 deletions(-) diff --git a/public/TracyClient.cpp b/public/TracyClient.cpp index 70bd17456e..400caab07e 100644 --- a/public/TracyClient.cpp +++ b/public/TracyClient.cpp @@ -32,27 +32,8 @@ #include "client/TracyOverride.cpp" #include "client/TracyKCore.cpp" -#if defined(TRACY_HAS_CALLSTACK) -# if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 -# include "libbacktrace/alloc.cpp" -# include "libbacktrace/dwarf.cpp" -# include "libbacktrace/fileline.cpp" -# include "libbacktrace/mmapio.cpp" -# include "libbacktrace/posix.cpp" -# include "libbacktrace/sort.cpp" -# include "libbacktrace/state.cpp" -# if TRACY_HAS_CALLSTACK == 4 -# include "libbacktrace/macho.cpp" -# else -# include "libbacktrace/elf.cpp" -# endif -# include "common/TracyStackFrames.cpp" -# endif -#endif - #ifdef _MSC_VER # pragma comment(lib, "ws2_32.lib") -# pragma comment(lib, "dbghelp.lib") # pragma comment(lib, "advapi32.lib") # pragma comment(lib, "user32.lib") # pragma warning(pop) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index ba9411a57e..4ff46d0198 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -4069,32 +4069,33 @@ void Profiler::ReportTopology() void Profiler::SendModulesCaches() { +#ifdef TRACY_HAS_CALLSTACK // We are retrieving modules information from the Tracy Profiler thread // But the Symbol Worker has ownership. Make sure it is not writing to the cache while we read it. std::lock_guard mutexguard{ GetModuleCacheMutexForRead() }; - std::vector sendQueuBuffer; - size_t moduleCount = -1; - - - const FastVector& kernelDrivers = GetKernelDriver(); - for (auto& it : kernelDrivers) + const FastVector* kernelDrivers = GetKernelImageInfos(); + if(kernelDrivers) { - SendModuleInfo(it, &sendQueuBuffer); + for (auto& it : *kernelDrivers) + { + SendModuleInfo(it); + } } - const FastVector& cacheModule = GetModuleData(); - // Sennd all moduleInfo - for (auto& it : cacheModule) + const FastVector* moduleCache = GetUserImageInfos(); + if(moduleCache) { - SendModuleInfo(it, &sendQueuBuffer); - } - - - + for (auto& it : *moduleCache) + { + SendModuleInfo(it); + } + } +#endif } +#ifdef TRACY_HAS_CALLSTACK void Profiler::WriteDebugFieldToPacket(uint8_t** ptr, int* currentPacketSize, const DegugModuleField& degugModuleField) { MemWrite(*ptr, degugModuleField.debugFormat); @@ -4107,50 +4108,50 @@ void Profiler::WriteDebugFieldToPacket(uint8_t** ptr, int* currentPacketSize, co *ptr += sizeof(dataSize); *currentPacketSize += sizeof(dataSize); - const uint8_t* data = degugModuleField.debugData; - - memcpy(*ptr, data, dataSize); + memcpy(*ptr, degugModuleField.debugData, dataSize); *ptr += dataSize; *currentPacketSize += dataSize; } -void Profiler::SendModuleInfo(const ModuleCacheEntry& moduleCacheEntry, std::vector* queuBuffer) +void Profiler::SendModuleInfo(const ImageEntry& imageEntry) { static constexpr int EndOfString = 1; - const uint32_t moduleNameLength = strlen(moduleCacheEntry.name) + EndOfString; - const uint32_t modulePathLength = strlen(moduleCacheEntry.path) + EndOfString; + const uint32_t moduleNameLength = (imageEntry.name ? strlen(imageEntry.name) : 0) + EndOfString; + const uint32_t modulePathLength = (imageEntry.path ? strlen(imageEntry.path) : 0) + EndOfString; QueueItem item; tracy::MemWrite(&item.hdr.type, (int)QueueType::DataPacket); - const size_t baseModuleInfo = sizeof(moduleCacheEntry.start) + sizeof(moduleCacheEntry.end) + + const size_t baseModuleInfo = sizeof(imageEntry.start) + sizeof(imageEntry.end) + sizeof(moduleNameLength) + moduleNameLength + sizeof(modulePathLength) + modulePathLength + - sizeof(moduleCacheEntry.degugModuleField.debugFormat); + sizeof(imageEntry.degugModuleField.debugFormat); const uint32_t debugFormatSize = 0 + sizeof(uint32_t) // DebugDataSize - + static_cast(moduleCacheEntry.degugModuleField.debugDataSize); + + static_cast(imageEntry.degugModuleField.debugDataSize); + + const size_t bufferSize = baseModuleInfo + debugFormatSize; + void* queueBuffer = tracy_malloc(bufferSize); - queuBuffer->resize(baseModuleInfo + debugFormatSize); int size = 0; - uint8_t* ptr = queuBuffer->data(); + uint8_t* ptr = (uint8_t*)queueBuffer; // baseModuleInfo - MemWrite(ptr, moduleCacheEntry.start); - ptr += sizeof(moduleCacheEntry.start); - size += sizeof(moduleCacheEntry.start); + MemWrite(ptr, imageEntry.start); + ptr += sizeof(imageEntry.start); + size += sizeof(imageEntry.start); - MemWrite(ptr, moduleCacheEntry.end); - ptr += sizeof(moduleCacheEntry.end); - size += sizeof(moduleCacheEntry.end); + MemWrite(ptr, imageEntry.end); + ptr += sizeof(imageEntry.end); + size += sizeof(imageEntry.end); MemWrite(ptr, moduleNameLength); ptr += sizeof(moduleNameLength); size += sizeof(moduleNameLength); - memcpy(ptr, moduleCacheEntry.name, moduleNameLength); + memcpy(ptr, imageEntry.name ? imageEntry.name : "", moduleNameLength); ptr += moduleNameLength; size += moduleNameLength; @@ -4158,16 +4159,16 @@ void Profiler::SendModuleInfo(const ModuleCacheEntry& moduleCacheEntry, std::vec ptr += sizeof(modulePathLength); size += sizeof(modulePathLength); - memcpy(ptr, moduleCacheEntry.path, modulePathLength); + memcpy(ptr, imageEntry.path ? imageEntry.path : "", modulePathLength); ptr += modulePathLength; size += modulePathLength; - WriteDebugFieldToPacket(&ptr, &size, moduleCacheEntry.degugModuleField); + WriteDebugFieldToPacket(&ptr, &size, imageEntry.degugModuleField); - assert(size == queuBuffer->size()); + assert(size == bufferSize); - SendSingleDataPacket(queuBuffer->data(), queuBuffer->size(), PacketDataType::ModuleInfo); + SendSingleDataPacket(queueBuffer, bufferSize, PacketDataType::ImageEntry); QueueItem item2; tracy::MemWrite(&item2.hdr.type, (int)QueueType::ModuleUpdate); @@ -4176,7 +4177,10 @@ void Profiler::SendModuleInfo(const ModuleCacheEntry& moduleCacheEntry, std::vec NeedDataSize(sizeof(item2)); AppendData(&item2, sizeof(item2)); + + tracy_free(queueBuffer); } +#endif void Profiler::SendCallstack( int32_t depth, const char* skipBefore ) { diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index 3d6f799dec..579372c488 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -6,7 +6,6 @@ #include #include #include -#include #include "tracy_concurrentqueue.h" #include "tracy_SPSCQueue.h" @@ -922,9 +921,10 @@ class Profiler void SendModulesCaches(); +#ifdef TRACY_HAS_CALLSTACK void WriteDebugFieldToPacket(uint8_t** ptr, int* currentPacketSize, const DegugModuleField& degugModuleField); - void SendModuleInfo(const ModuleCacheEntry& moduleCacheEntry, std::vector* queuBuffer); - + void SendModuleInfo(const ImageEntry& moduleCacheEntry); +#endif static tracy_force_inline void SendCallstackSerial( void* ptr ) { diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index ab9539e5cb..319c6e87bc 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -11,15 +11,6 @@ #include "TracySystem.hpp" #include "TracyDebugModulesHeaderFile.hpp" - - -# pragma optimize( "", off ) - - - - - - #ifdef TRACY_HAS_CALLSTACK constexpr uint32_t ImageCacheBaseCapacity = 512; @@ -36,15 +27,32 @@ constexpr uint32_t ImageCacheBaseCapacity = 512; # pragma warning( disable : 4091 ) # endif # include +# pragma comment( lib, "dbghelp.lib" ) # ifdef _MSC_VER # pragma warning( pop ) # endif -#elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 +#elif defined(TRACY_USE_LIBBACKTRACE) + # include "../libbacktrace/backtrace.hpp" # include # include # include # include + +# include "../libbacktrace/alloc.cpp" +# include "../libbacktrace/dwarf.cpp" +# include "../libbacktrace/fileline.cpp" +# include "../libbacktrace/mmapio.cpp" +# include "../libbacktrace/posix.cpp" +# include "../libbacktrace/sort.cpp" +# include "../libbacktrace/state.cpp" +# if TRACY_HAS_CALLSTACK == 4 +# include "../libbacktrace/macho.cpp" +# else +# include "../libbacktrace/elf.cpp" +# endif +# include "TracyStackFrames.cpp" + #elif TRACY_HAS_CALLSTACK == 5 # include # include @@ -65,41 +73,79 @@ extern "C" }; #endif -//#define TRACY_USE_IMAGE_CACHE +#if defined(TRACY_USE_LIBBACKTRACE) || TRACY_HAS_CALLSTACK == 5 +// If you want to use your own demangling functionality (e.g. for another language), +// define TRACY_DEMANGLE and provide your own implementation of the __tracy_demangle +// function. The input parameter is a function name. The demangle function must +// identify whether this name is mangled, and fail if it is not. Failure is indicated +// by returning nullptr. If demangling succeeds, a pointer to the C string containing +// demangled function must be returned. The demangling function is responsible for +// managing memory for this string. It is expected that it will be internally reused. +// When a call to ___tracy_demangle is made, previous contents of the string memory +// do not need to be preserved. Function may return string of any length, but the +// profiler can choose to truncate it. +extern "C" const char* ___tracy_demangle( const char* mangled ); + +#ifndef TRACY_DEMANGLE +constexpr size_t ___tracy_demangle_buffer_len = 1024*1024; +char* ___tracy_demangle_buffer; -namespace tracy +void ___tracy_init_demangle_buffer() { - // 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; + ___tracy_demangle_buffer = (char*)tracy::tracy_malloc( ___tracy_demangle_buffer_len ); +} - bool s_serverLocalResolve = true; // Should the profiler try to resolve symbols before querying the client for symbols. +void ___tracy_free_demangle_buffer() +{ + tracy::tracy_free( ___tracy_demangle_buffer ); +} + +extern "C" const char* ___tracy_demangle( const char* mangled ) +{ + if( !mangled || mangled[0] != '_' ) return nullptr; + if( strlen( mangled ) > ___tracy_demangle_buffer_len ) return nullptr; + int status; + size_t len = ___tracy_demangle_buffer_len; + return abi::__cxa_demangle( mangled, ___tracy_demangle_buffer, &len, &status ); +} +#endif +#endif - inline bool IsEnv(const char* environementVariableName, bool defaultValue) +#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_USE_IMAGE_CACHE +# include +#endif + +namespace tracy +{ + + inline bool GetEnvBool(const char* environementVariableName, bool defaultValue) { const char* v = GetEnvVar(environementVariableName); if (v) return v[0] == '1'; else return defaultValue; } - struct ModuleNameAndBaseAddress + // 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* name; - uint64_t baseAddr; - }; + return GetEnvBool("TRACY_SYMBOL_OFFLINE_RESOLVE", false); + } +#endif // TRACY_SYMBOL_OFFLINE_RESOLVE - // The only threads that access the cache are the Symbol Worker and the Tracy Thread - // Only the symbol worker may write, but the Tracy Thread needs to be able to read the cache safely - // Since only the SymbolWorker is allowed to write the cache, it does not need to lock when reading. - static std::recursive_mutex s_cacheMutex; - std::recursive_mutex& GetModuleCacheMutexForRead() { - return s_cacheMutex; + bool s_serverLocalResolve = true; // Should the profiler try to resolve symbols before querying the client for symbols. + + inline bool IsKernelAddress(uint64_t addr) { + return (addr >> 63) != 0; } - void DestroyModuleCacheEntry(ModuleCacheEntry& entry) + void DestroyImageEntry(ImageEntry& entry) { tracy_free_fast(entry.path); entry.path = nullptr; @@ -112,160 +158,104 @@ namespace tracy class ImageCache { public: - const ModuleCacheEntry* FindEntryFromAddr(uint64_t addr) const - { - for (size_t i = 0; i < m_modCache.size(); i++) - { - auto& it = m_modCache[i]; - - if (addr >= it.start && addr < it.end) - return ⁢ - - } + ImageCache(size_t moduleCacheCapacity = ImageCacheBaseCapacity) : m_modCache(moduleCacheCapacity) {} + ~ImageCache() { Clear(); } - auto it = std::lower_bound(m_modCache.begin(), m_modCache.end(), addr, [](const ModuleCacheEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); - if (it != m_modCache.end() && (addr < it->end)) - return &(*it); - - return nullptr; + ImageEntry* CacheModuleWithDebugInfo(const ImageEntry& entry) + { + m_sorted &= m_modCache.empty() ? true : (entry.start < m_modCache.back().start); + ImageEntry* newEntry = m_modCache.push_next(); + *newEntry = entry; + return newEntry; } - - void MapModuleData(const ModuleCacheEntry** moduleCacheEntries, size_t* moduleCount) + const ImageEntry* FindEntryFromAddr(uint64_t addr) const { - if (m_modCache.empty()) + if (m_sorted) { - *moduleCacheEntries = nullptr; - *moduleCount = 0; - return; + auto it = std::lower_bound(m_modCache.begin(), m_modCache.end(), addr, [](const ImageEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); + if (it != m_modCache.end() && (addr < it->end || it->end == 0)) + return &(*it); } - - *moduleCacheEntries = m_modCache.data(); - *moduleCount = m_modCache.size(); + else + { + auto it = std::find_if(m_modCache.begin(), m_modCache.end(), [addr](const ImageEntry& module) { return addr >= module.start && (addr < module.end || module.end == 0); }); + if (it != m_modCache.end()) + return &(*it); + } + return nullptr; } - ModuleCacheEntry* CacheModuleWithDebugInfo(const ModuleCacheEntry& entry) + void Sort() { - ModuleCacheEntry* newEntry = m_modCache.push_next(); - *newEntry = entry; - return newEntry; + if (!m_sorted) + { + std::sort(m_modCache.begin(), m_modCache.end(), [](const ImageEntry& lhs, const ImageEntry& rhs) { return lhs.start > rhs.start; }); + m_sorted = true; + } } void Clear() { - for (ModuleCacheEntry& cacheEntry : m_modCache) + for (ImageEntry& cacheEntry : m_modCache) { - DestroyModuleCacheEntry(cacheEntry); + DestroyImageEntry(cacheEntry); } m_modCache.clear(); + m_sorted = true; } - bool Contain(const ModuleCacheEntry& moduleCacheEntry) + bool ContainsModule(uint64_t startAddress) { - return std::find_if(m_modCache.begin(), m_modCache.end(), [moduleCacheEntry](const ModuleCacheEntry& module)->bool - { - return moduleCacheEntry.start == moduleCacheEntry.start; - }) != m_modCache.end(); + const ImageEntry* moduleInfo = FindEntryFromAddr(startAddress); + return moduleInfo && moduleInfo->start == startAddress; } - const FastVector& GetModuleData() const + const FastVector& GetModuleData() const { return m_modCache; } - - ImageCache(size_t moduleCacheCapacity) : m_modCache(moduleCacheCapacity) - { - - } - - - ~ImageCache() - { - Clear(); - } - - protected: - FastVector m_modCache; - + FastVector m_modCache; + bool m_sorted = true; }; -} - - -#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 5 || TRACY_HAS_CALLSTACK == 6 -// If you want to use your own demangling functionality (e.g. for another language), -// define TRACY_DEMANGLE and provide your own implementation of the __tracy_demangle -// function. The input parameter is a function name. The demangle function must -// identify whether this name is mangled, and fail if it is not. Failure is indicated -// by returning nullptr. If demangling succeeds, a pointer to the C string containing -// demangled function must be returned. The demangling function is responsible for -// managing memory for this string. It is expected that it will be internally reused. -// When a call to ___tracy_demangle is made, previous contents of the string memory -// do not need to be preserved. Function may return string of any length, but the -// profiler can choose to truncate it. -extern "C" const char* ___tracy_demangle( const char* mangled ); - -#ifndef TRACY_DEMANGLE -constexpr size_t ___tracy_demangle_buffer_len = 1024*1024; -char* ___tracy_demangle_buffer; - -void ___tracy_init_demangle_buffer() -{ - ___tracy_demangle_buffer = (char*)tracy::tracy_malloc( ___tracy_demangle_buffer_len ); -} - -void ___tracy_free_demangle_buffer() -{ - tracy::tracy_free( ___tracy_demangle_buffer ); -} - -extern "C" const char* ___tracy_demangle( const char* mangled ) -{ - if( !mangled || mangled[0] != '_' ) return nullptr; - if( strlen( mangled ) > ___tracy_demangle_buffer_len ) return nullptr; - int status; - size_t len = ___tracy_demangle_buffer_len; - return abi::__cxa_demangle( mangled, ___tracy_demangle_buffer, &len, &status ); -} -#endif -#endif -#if TRACY_HAS_CALLSTACK == 3 -# define TRACY_USE_IMAGE_CACHE -# include -#endif -namespace tracy -{ + // The only threads that access the cache are the Symbol Worker and the Tracy Thread + // Only the symbol worker may write, but the Tracy Thread needs to be able to read the cache safely + // Since only the SymbolWorker is allowed to write the cache, it does not need to lock when reading. + static std::recursive_mutex s_cacheMutex; + std::recursive_mutex& GetModuleCacheMutexForRead() { + return s_cacheMutex; + } -#ifdef TRACY_USE_IMAGE_CACHE +#if defined(TRACY_USE_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 ImageCacheLinux : public ImageCache +class ImageCacheLibbacktrace : public ImageCache { public: - ImageCacheLinux() - : ImageCache(ImageCacheAllocSize) + ImageCacheLibbacktrace() + : ImageCache() { - // ?? Refresh(); } - ~ImageCacheLinux() + ~ImageCacheLibbacktrace() { m_haveMainImageName = false; } - const ModuleCacheEntry* GetImageForAddress( void* address ) + const ImageEntry* GetImageForAddress( uint64_t address ) { - const ModuleCacheEntry* entry = FindEntryFromAddr( (uint64_t)address ); + const ImageEntry* entry = FindEntryFromAddr( address ); - /*if (!entry) + if (!entry) { Refresh(); - return GetImageForAddressImpl( address ); - }*/ + return FindEntryFromAddr( address ); + } return entry; } @@ -273,42 +263,34 @@ class ImageCacheLinux : public ImageCache bool m_updated = false; bool m_haveMainImageName = false; - bool Contains(void* startAddress) const - { - uint64_t address = (uint64_t)startAddress; - return std::any_of(m_modCache.begin(), m_modCache.end(), [address](const ModuleCacheEntry& entry) { return address == entry.start; }); - } - static int Callback( struct dl_phdr_info* info, size_t size, void* data ) { - // SCARY - ImageCacheLinux* cache = reinterpret_cast( data ); + ImageCacheLibbacktrace* cache = reinterpret_cast( data ); - const auto startAddress = reinterpret_cast( info->dlpi_addr ); - if( cache->Contains( startAddress ) ) return 0; + const uint64_t startAddress = reinterpret_cast( info->dlpi_addr ); + if( cache->ContainsModule( startAddress ) ) return 0; const uint32_t headerCount = info->dlpi_phnum; assert( headerCount > 0); - const auto endAddress = reinterpret_cast( info->dlpi_addr + + const uint64_t endAddress = reinterpret_cast( info->dlpi_addr + info->dlpi_phdr[info->dlpi_phnum - 1].p_vaddr + info->dlpi_phdr[info->dlpi_phnum - 1].p_memsz); - ImageEntry* image = cache->m_images.push_next(); - image->m_startAddress = startAddress; - image->m_endAddress = endAddress; + ImageEntry image{}; + image.start = startAddress; + image.end = 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 if( info->dlpi_name && info->dlpi_name[0] != '\0' ) { - size_t sz = strlen( info->dlpi_name ) + 1; - image->m_name = (char*)tracy_malloc( sz ); - memcpy( image->m_name, info->dlpi_name, sz ); + image.name = CopyStringFast(info->dlpi_name); } else { - image->m_name = nullptr; + image.name = nullptr; } + cache->CacheModuleWithDebugInfo(image); cache->m_updated = true; return 0; @@ -323,9 +305,7 @@ class ImageCacheLinux : public ImageCache if( m_updated ) { - std::sort( m_modCache.begin(), m_modCache.end(), - []( const ModuleCacheEntry& lhs, const ModuleCacheEntry& rhs ) { return lhs.start > rhs.start; } ); - + Sort(); // patch the main executable image name here, as calling dl_* functions inside the dl_iterate_phdr callback might cause deadlocks UpdateMainImageName(); } @@ -338,7 +318,7 @@ class ImageCacheLinux : public ImageCache return; } - for(ModuleCacheEntry& entry : m_modCache) + for(ImageEntry& entry : m_modCache) { if( entry.name == nullptr ) { @@ -347,9 +327,7 @@ class ImageCacheLinux : public ImageCache { if( dlInfo.dli_fname ) { - size_t sz = strlen( dlInfo.dli_fname ) + 1; - entry.name = (char*)tracy_malloc( sz ); - memcpy( entry.name, dlInfo.dli_fname, sz ); + entry.name = CopyStringFast(dlInfo.dli_fname); } } @@ -363,20 +341,60 @@ class ImageCacheLinux : public ImageCache void Clear() { - for( ImageEntry& entry : m_images ) - { - tracy_free( entry.m_name ); - } - - m_images.clear(); + ImageCache::Clear(); m_haveMainImageName = false; } }; +#endif // defined(TRACY_USE_IMAGE_CACHE) + + +#ifdef TRACY_USE_IMAGE_CACHE +typedef ImageCacheLibbacktrace UserlandImageCache; +#else +typedef ImageCache UserlandImageCache; #endif //#ifdef TRACY_USE_IMAGE_CACHE +static UserlandImageCache* s_imageCache = nullptr; +static ImageCache* s_krnlCache = nullptr; + +#ifdef __linux +static ImageCache* s_krnlSymbolsCache = nullptr; +#endif + +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((void*)s_krnlCache); + s_krnlCache = nullptr; + } + + if (s_imageCache != nullptr) + { + s_imageCache->~UserlandImageCache(); + tracy_free(s_imageCache); + s_imageCache = nullptr; + } +} +const FastVector* GetUserImageInfos() +{ + return &s_imageCache->GetModuleData(); +} +const FastVector* GetKernelImageInfos() +{ + return &s_krnlCache->GetModuleData(); +} void FormatImageName(char** moduleCacheName, const char* imageName, uint32_t imageNameLength) { @@ -391,12 +409,6 @@ void FormatImageName(char** moduleCacheName, const char* imageName, uint32_t ima (*moduleCacheName)[namelen + 2] = '\0'; } -bool ShouldResolveSymbolsOffline() -{ - return IsEnv("TRACY_SYMBOL_OFFLINE_RESOLVE", false); -} -#endif // #ifdef TRACY_SYMBOL_OFFLINE_RESOLVE - #if TRACY_HAS_CALLSTACK == 1 enum { MaxCbTrace = 64 }; @@ -406,7 +418,6 @@ int cb_num; CallstackEntry cb_data[MaxCbTrace]; HANDLE s_DbgHelpSymHandle = 0; -#pragma comment( lib, "dbghelp.lib" ) extern "C" { @@ -434,14 +445,6 @@ struct CV_INFO_PDB70 static constexpr auto mandatoryAlignment = 8; static constexpr DWORD CV_SIGNATURE_RSDS = 'SDSR'; // 'SDSR' - - -FastVector* s_krnlCache = nullptr; - - - -ImageCache* s_imageCacheWindows = nullptr; - uint64_t DbgHelpLoadSymbolsForModule(const char* imageName, uint64_t baseOfDll, uint32_t dllSize) { auto d = SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0); @@ -520,7 +523,7 @@ bool GetModuleInfoFromPEHeaders(uint64_t baseOfDll, DebugFormat* debugFormat, ui return false; } -void GetModuleInfoFromDbgHelp(const char* imageName, ModuleCacheEntry* moduleEntry) +void GetModuleInfoFromDbgHelp(const char* imageName, ImageEntry* moduleEntry) { IMAGEHLP_MODULE64 moduleInfo{}; moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); @@ -567,10 +570,10 @@ void GetModuleInfoFromDbgHelp(const char* imageName, ModuleCacheEntry* moduleEnt } } -ModuleCacheEntry* CacheModuleInfo(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) +ImageEntry* CacheModuleInfo(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) { - ModuleCacheEntry moduleEntry = {}; + ImageEntry moduleEntry = {}; moduleEntry.start = baseOfDll; moduleEntry.end = baseOfDll + dllSize; moduleEntry.path = CopyStringFast(imageName, imageNameLength); @@ -590,16 +593,22 @@ ModuleCacheEntry* CacheModuleInfo(const char* imageName, uint32_t imageNameLengt } std::lock_guard mutexguard{ s_cacheMutex }; - return s_imageCacheWindows->CacheModuleWithDebugInfo(moduleEntry); + return s_imageCache->CacheModuleWithDebugInfo(moduleEntry); } -ModuleCacheEntry* LoadSymbolsForModuleAndCache(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) +ImageEntry* LoadSymbolsForModuleAndCache(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) { DbgHelpLoadSymbolsForModule(imageName, baseOfDll, dllSize); return CacheModuleInfo(imageName, imageNameLength, baseOfDll, dllSize); } +struct ModuleNameAndBaseAddress +{ + const char* name; + uint64_t baseAddr; +}; + ModuleNameAndBaseAddress OnFailedFindAddress(uint64_t address) { InitRpmalloc(); @@ -623,8 +632,8 @@ ModuleNameAndBaseAddress OnFailedFindAddress(uint64_t address) if (nameLength > 0) { // since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize) - ModuleCacheEntry* moduleCacheEntry = LoadSymbolsForModuleAndCache(name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage); - return ModuleNameAndBaseAddress{ moduleCacheEntry->name, moduleCacheEntry->start }; + ImageEntry* ImageEntry = LoadSymbolsForModuleAndCache(name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage); + return ModuleNameAndBaseAddress{ ImageEntry->name, ImageEntry->start }; } } } @@ -635,20 +644,20 @@ ModuleNameAndBaseAddress OnFailedFindAddress(uint64_t address) ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols(uint64_t addr, bool* failed) { - if ((addr >> 63) != 0) + if (IsKernelAddress(addr)) { if (s_krnlCache) { - auto it = std::lower_bound(s_krnlCache->begin(), s_krnlCache->end(), addr, [](const ModuleCacheEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); - if (it != s_krnlCache->end()) + const ImageEntry* entry = s_krnlCache->FindEntryFromAddr(addr); + if (entry) { - return ModuleNameAndBaseAddress{ it->name, it->start }; + return ModuleNameAndBaseAddress{ entry->name, entry->start }; } } return ModuleNameAndBaseAddress{ "", addr }; } - const ModuleCacheEntry* entry = s_imageCacheWindows->FindEntryFromAddr(addr); + const ImageEntry* entry = s_imageCache->FindEntryFromAddr(addr); if (entry != nullptr) return ModuleNameAndBaseAddress{ entry->name, entry->start }; @@ -734,13 +743,13 @@ static void CacheProcessDrivers() buf[0] = '<'; memcpy(buf + 1, fn, len); memcpy(buf + len + 1, ">", 2); - ModuleCacheEntry* kernelDriver = s_krnlCache->push_next(); + ImageEntry kernelDriver{}; - kernelDriver->start = (uint64_t)dev[i]; - kernelDriver->end = 0; - kernelDriver->name = buf; - kernelDriver->path = nullptr; - kernelDriver->degugModuleField = {}; + kernelDriver.start = (uint64_t)dev[i]; + kernelDriver.end = 0; + kernelDriver.name = buf; + kernelDriver.path = nullptr; + kernelDriver.degugModuleField = {}; const auto len = GetDeviceDriverFileNameA( dev[i], fn, sizeof( fn ) ); if( len != 0 ) @@ -755,7 +764,7 @@ static void CacheProcessDrivers() path = full; } - kernelDriver->path = CopyStringFast(path); + kernelDriver.path = CopyStringFast(path); if (SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0)) { @@ -768,11 +777,12 @@ static void CacheProcessDrivers() } } - assert(kernelDriver->end == 0 && "kernel end should be zero"); + s_krnlCache->CacheModuleWithDebugInfo(kernelDriver); + assert(kernelDriver.end == 0 && "kernel end should be zero"); cnt++; } } - std::sort(s_krnlCache->begin(), s_krnlCache->end(), [](const ModuleCacheEntry& lhs, const ModuleCacheEntry& rhs) { return lhs.start > rhs.start; }); + s_krnlCache->Sort(); } } @@ -795,27 +805,17 @@ static void CacheProcessModules() { // This may be a new module loaded since our call to SymInitialize. // Just in case, force DbgHelp to load its pdb ! - - if (!s_shouldResolveSymbolsOffline) - { - auto moduleCache = LoadSymbolsForModuleAndCache(name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage); - - } - else - { - uint64_t baseAdd = (DWORD64)info.lpBaseOfDll; - - } - + LoadSymbolsForModuleAndCache(name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage); } } } + s_imageCache->Sort(); } } void InitCallstack() { - s_serverLocalResolve = IsEnv("SERVER_LOCAL_RESOLVE", true); + s_serverLocalResolve = GetEnvBool("SERVER_LOCAL_RESOLVE", true); #ifndef TRACY_SYMBOL_OFFLINE_RESOLVE @@ -833,11 +833,8 @@ void InitCallstack() DbgHelpInit((HANDLE)42/*This is our fake DbgHelp Handle*/, false /* Don't invade the process, we're going to resolve symbols for another one*/); #endif - s_imageCacheWindows = (ImageCache*)tracy_malloc(sizeof(ImageCache)); - new(s_imageCacheWindows) ImageCache(ImageCacheBaseCapacity); - s_krnlCache = (FastVector*)tracy_malloc(sizeof(FastVector)); - new(s_krnlCache) FastVector(ImageCacheBaseCapacity); - + CreateImageCaches(); + #ifdef TRACY_ENABLE // Client or self-profiling server #ifdef TRACY_DBGHELP_LOCK DBGHELP_LOCK; @@ -867,31 +864,9 @@ void InitCallstack() #endif } -void FreeKernelCache() -{ - if (s_krnlCache == nullptr) - { - return; - } - - for (auto& it : *s_krnlCache) - { - DestroyModuleCacheEntry(it); - } - tracy_free((void*)s_krnlCache); - s_krnlCache = nullptr; -} - void EndCallstack() { - FreeKernelCache(); - - if (s_imageCacheWindows != nullptr) - { - s_imageCacheWindows->~ImageCache(); - tracy_free(s_imageCacheWindows); - s_imageCacheWindows = nullptr; - } + DestroyImageCaches(); if (s_DbgHelpSymHandle) { SymCleanup(s_DbgHelpSymHandle); @@ -933,11 +908,13 @@ const char* GetKernelModulePath(uint64_t addr) { assert(addr >> 63 != 0); if (!s_krnlCache) return nullptr; - auto it = std::lower_bound(s_krnlCache->begin(), s_krnlCache->end(), addr, [](const ModuleCacheEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); - if (it == s_krnlCache->end()) return nullptr; - return it->path; + const ImageEntry* imageEntry = s_krnlCache->FindEntryFromAddr(addr); + if (imageEntry) + return imageEntry->path; + return nullptr; } + bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, DebugFormat debugFormat, const uint8_t* debugData, uint32_t debugDataSize) { assert(debugFormat == DebugFormat::PdbDebugFormat); @@ -1005,48 +982,41 @@ bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, return false; } -bool IsKernelAddress(uint64_t addr) { - return (addr >> 63) != 0; -} - // Called from the profiler (server) only, we received data from the client. -void CacheModuleAndLoadExternal(ModuleCacheEntry& moduleCacheEntry) +void CacheModuleAndLoadExternal(ImageEntry& imageEntry) { if (!s_serverLocalResolve) { - DestroyModuleCacheEntry(moduleCacheEntry); + DestroyImageEntry(imageEntry); return; } -#if 1 // windows - if (IsKernelAddress(moduleCacheEntry.start)) + if (IsKernelAddress(imageEntry.start)) { - ModuleCacheEntry& newKernel = *s_krnlCache->push_next(); - newKernel = moduleCacheEntry; + s_krnlCache->CacheModuleWithDebugInfo(imageEntry); } else { - s_imageCacheWindows->CacheModuleWithDebugInfo(moduleCacheEntry); + s_imageCache->CacheModuleWithDebugInfo(imageEntry); } -#endif bool hasSymbolInfo = false; - if (moduleCacheEntry.degugModuleField.debugFormat == DebugFormat::PdbDebugFormat) + if (imageEntry.degugModuleField.debugFormat == DebugFormat::PdbDebugFormat) { char* nameFixed = nullptr; - if (IsKernelAddress(moduleCacheEntry.start)) + if (IsKernelAddress(imageEntry.start)) { - const size_t nameLen = strlen(moduleCacheEntry.name); - if (nameLen > 3 && moduleCacheEntry.name[0] == '<' && moduleCacheEntry.name[nameLen - 1] == '>') + const size_t nameLen = strlen(imageEntry.name); + if (nameLen > 3 && imageEntry.name[0] == '<' && imageEntry.name[nameLen - 1] == '>') { - char* kernelName = CopyStringFast(moduleCacheEntry.name + 1); + char* kernelName = CopyStringFast(imageEntry.name + 1); kernelName[nameLen - 2] = 0; // replace > nameFixed = kernelName; } } - hasSymbolInfo = LoadFromPdb(nameFixed ? nameFixed : moduleCacheEntry.name, moduleCacheEntry.start, moduleCacheEntry.end - moduleCacheEntry.start, - moduleCacheEntry.degugModuleField.debugFormat, moduleCacheEntry.degugModuleField.debugData, moduleCacheEntry.degugModuleField.debugDataSize); + hasSymbolInfo = LoadFromPdb(nameFixed ? nameFixed : imageEntry.name, imageEntry.start, imageEntry.end - imageEntry.start, + imageEntry.degugModuleField.debugFormat, imageEntry.degugModuleField.debugData, imageEntry.degugModuleField.debugDataSize); if (nameFixed) tracy_free_fast(nameFixed); } @@ -1055,31 +1025,11 @@ void CacheModuleAndLoadExternal(ModuleCacheEntry& moduleCacheEntry) { // TODO: load from path only if we can check we got the correct binary (check timestamp / guid ?) // That would however require to disable deferred symbol loading or use FindExecutableImageEx - //DbgHelpLoadSymbolsForModule(moduleCacheEntry.path, moduleCacheEntry.start, moduleCacheEntry.end - moduleCacheEntry.start); + //DbgHelpLoadSymbolsForModule(ImageEntry.path, ImageEntry.start, ImageEntry.end - ImageEntry.start); } } -void CacheModuleKernelAndLoadExternal(const ModuleCacheEntry& kernelDriver) -{ - if(!s_serverLocalResolve) - return; - - - bool hasSymbolInfo = false; - if (kernelDriver.degugModuleField.debugFormat == DebugFormat::PdbDebugFormat) - { - hasSymbolInfo = LoadFromPdb(kernelDriver.path, kernelDriver.start, 0, kernelDriver.degugModuleField.debugFormat, kernelDriver.degugModuleField.debugData, kernelDriver.degugModuleField.debugDataSize); - } - - if (!hasSymbolInfo) - { - // TODO: load from path only if we can check we got the correct binary (check timestamp / guid ?) - //DbgHelpLoadSymbolsForModule(kernelDriver.path, kernelDriver.start, 0); - } - -} - CallstackSymbolData DecodeSymbolAddress(uint64_t ptr) { CallstackSymbolData sym; @@ -1117,16 +1067,6 @@ CallstackSymbolData DecodeSymbolAddress(uint64_t ptr) return sym; } -const FastVector& GetModuleData() -{ - return s_imageCacheWindows->GetModuleData(); -} - -const FastVector& GetKernelDriver() -{ - return *s_krnlCache; -} - CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus) { #ifdef TRACY_DBGHELP_LOCK @@ -1280,23 +1220,7 @@ CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _d return { cb_data, uint8_t(cb_num), moduleNameAndAddress.name }; } -void FindModuleFromAddr(uint64_t addr, const ModuleCacheEntry** entry) -{ - *entry = s_imageCacheWindows->FindEntryFromAddr(addr); -} - -void FindKernelDriverFromAddr(uint64_t addr, const ModuleCacheEntry** outDrive) -{ - auto it = std::lower_bound(s_krnlCache->begin(), s_krnlCache->end(), addr, [](const ModuleCacheEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); - if (it != s_krnlCache->end()) - { - *outDrive = it; - } -} - - - -#elif TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 +#elif defined(TRACY_USE_LIBBACKTRACE) enum { MaxCbTrace = 64 }; @@ -1305,9 +1229,8 @@ struct backtrace_state* cb_bts = nullptr; int cb_num; CallstackEntry cb_data[MaxCbTrace]; int cb_fixup; -#ifdef TRACY_USE_IMAGE_CACHE -static ImageCache* s_imageCache = nullptr; -#endif //#ifdef TRACY_USE_IMAGE_CACHE + +void CacheModuleAndLoadExternal(ImageEntry& ImageEntry) {} #ifdef TRACY_DEBUGINFOD debuginfod_client* s_debuginfod; @@ -1324,22 +1247,17 @@ static FastVector* s_di_known; #endif #ifdef __linux -struct KernelSymbol -{ - uint64_t addr; - uint32_t size; - const char* name; - const char* mod; -}; - -KernelSymbol* s_kernelSym = nullptr; -size_t s_kernelSymCnt; static void InitKernelSymbols() { + if (s_krnlSymbolsCache == nullptr) + { + s_krnlSymbolsCache = new(tracy_malloc(sizeof(ImageCache))) ImageCache(ImageCacheBaseCapacity); + } + FILE* f = fopen( "/proc/kallsyms", "rb" ); if( !f ) return; - tracy::FastVector tmpSym( 512 * 1024 ); + tracy::FastVector tmpSym( 512 * 1024 ); size_t linelen = 16 * 1024; // linelen must be big enough to prevent reallocs in getline() auto linebuf = (char*)tracy_malloc( linelen ); ssize_t sz; @@ -1410,30 +1328,20 @@ static void InitKernelSymbols() } } - auto sym = tmpSym.push_next(); - sym->addr = addr; - sym->size = 0; - sym->name = strname; - sym->mod = strmod; + // Note: This uses the image cache as a symbol cache. + ImageEntry kernelSymbol{}; + kernelSymbol.start = addr; + kernelSymbol.end = 0; + kernelSymbol.name = strname; + kernelSymbol.path = strmod; + + s_krnlSymbolsCache->CacheModuleWithDebugInfo(kernelSymbol); + } tracy_free_fast( linebuf ); fclose( f ); - if( tmpSym.empty() ) return; - std::sort( tmpSym.begin(), tmpSym.end(), []( const KernelSymbol& lhs, const KernelSymbol& rhs ) { return lhs.addr < rhs.addr; } ); - for( size_t i=0; iSort(); TracyDebug( "Loaded %zu kernel symbols (%zu code sections)\n", tmpSym.size(), validCnt ); } @@ -1503,8 +1411,7 @@ void InitCallstack() InitRpmalloc(); #ifdef TRACY_USE_IMAGE_CACHE - s_imageCache = (ImageCache*)tracy_malloc( sizeof( ImageCache ) ); - new(s_imageCache) ImageCache(); + CreateImageCaches(); #endif //#ifdef TRACY_USE_IMAGE_CACHE #ifndef TRACY_SYMBOL_OFFLINE_RESOLVE @@ -1600,11 +1507,7 @@ debuginfod_client* GetDebuginfodClient() void EndCallstack() { #ifdef TRACY_USE_IMAGE_CACHE - if( s_imageCache ) - { - s_imageCache->~ImageCache(); - tracy_free( s_imageCache ); - } + DestroyImageCaches(); #endif //#ifdef TRACY_USE_IMAGE_CACHE #ifndef TRACY_DEMANGLE ___tracy_free_demangle_buffer(); @@ -1792,20 +1695,23 @@ void GetSymbolForOfflineResolve(void* address, uint64_t imageBaseAddress, Callst cbEntry.line = 0; } -CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) +CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus) { InitRpmalloc(); + +#ifdef TRACY_ENABLE // Only works on the current process. Profiler does not have TRACY_ENABLE, or is self profiling + if( ptr >> 63 == 0 ) { const char* imageName = nullptr; uint64_t imageBaseAddress = 0x0; #ifdef TRACY_USE_IMAGE_CACHE - const auto* image = s_imageCache->GetImageForAddress((void*)ptr); + const auto* image = s_imageCache->GetImageForAddress(ptr); if( image ) { - imageName = image->m_name; - imageBaseAddress = uint64_t(image->m_startAddress); + imageName = image->name; + imageBaseAddress = uint64_t(image->start); } #else Dl_info dlinfo; @@ -1830,34 +1736,40 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr ); } + *_decodeCallStackPtrStatus = imageName ? DecodeCallStackPtrStatus::Success : DecodeCallStackPtrStatus::SymbolMissing; return { cb_data, uint8_t( cb_num ), imageName ? imageName : "[unknown]" }; } #ifdef __linux - else if( s_kernelSym ) + else if(s_krnlSymbolsCache) { - auto it = std::lower_bound( s_kernelSym, s_kernelSym + s_kernelSymCnt, ptr, []( const KernelSymbol& lhs, const uint64_t& rhs ) { return lhs.addr + lhs.size < rhs; } ); - if( it != s_kernelSym + s_kernelSymCnt ) + // This is a symbol cache, kernel image cache is currently unused + const ImageEntry* symbolEntry = s_krnlSymbolsCache->FindEntryFromAddr((uint64_t)ptr); + if (symbolEntry) { - cb_data[0].name = CopyStringFast( it->name ); + cb_data[0].name = CopyStringFast(symbolEntry->name ); cb_data[0].file = CopyStringFast( "" ); cb_data[0].line = 0; - cb_data[0].symLen = it->size; - cb_data[0].symAddr = it->addr; - return { cb_data, 1, it->mod ? it->mod : "" }; + cb_data[0].symLen = symbolEntry->end - symbolEntry->start; + cb_data[0].symAddr = symbolEntry->start; + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; + return { cb_data, 1, symbolEntry->path ? symbolEntry->path : "" }; } } #endif - +#endif cb_data[0].name = CopyStringFast( "[unknown]" ); cb_data[0].file = CopyStringFast( "" ); cb_data[0].line = 0; cb_data[0].symLen = 0; cb_data[0].symAddr = 0; + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; return { cb_data, 1, "" }; } #elif TRACY_HAS_CALLSTACK == 5 +void CacheModuleAndLoadExternal(ImageEntry& ImageEntry) {} + void InitCallstackCritical() { } @@ -1902,7 +1814,7 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) return CallstackSymbolData { symloc, 0, false, 0 }; } -CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) +CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus) { static CallstackEntry cb; cb.line = 0; @@ -1948,6 +1860,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr ) cb.symLen = 0; cb.symAddr = (uint64_t)symaddr; + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; return { &cb, 1, symloc }; } diff --git a/public/common/TracyCallstack.h b/public/common/TracyCallstack.h index 2c7ecad9f3..bb02ed51c4 100644 --- a/public/common/TracyCallstack.h +++ b/public/common/TracyCallstack.h @@ -30,6 +30,10 @@ # define TRACY_HAS_CALLSTACK 6 # endif +#if TRACY_HAS_CALLSTACK == 2 || TRACY_HAS_CALLSTACK == 3 || TRACY_HAS_CALLSTACK == 4 || TRACY_HAS_CALLSTACK == 6 +#define TRACY_USE_LIBBACKTRACE +#endif + #endif #endif diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index 888ab3e05a..52e5b05670 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -85,7 +85,7 @@ struct DegugModuleField uint32_t debugDataSize; }; -struct ModuleCacheEntry +struct ImageEntry { uint64_t start; uint64_t end; @@ -107,14 +107,13 @@ void InitCallstack(); void InitCallstackCritical(); void EndCallstack(); const char* GetKernelModulePath( uint64_t addr ); -void FindModuleFromAddr(uint64_t addr, const ModuleCacheEntry** outModule); -void FindKernelDriverFromAddr(uint64_t addr, const ModuleCacheEntry** outDrive); +void FindModuleFromAddr(uint64_t addr, const ImageEntry** outModule); +void FindKernelDriverFromAddr(uint64_t addr, const ImageEntry** outDrive); -void CacheModuleAndLoadExternal(ModuleCacheEntry& moduleCacheEntry); -void CacheModuleKernelAndLoadExternal(const ModuleCacheEntry& kernelDriver); -const FastVector& GetModuleData(); -const FastVector& GetKernelDriver(); +void CacheModuleAndLoadExternal(ImageEntry& moduleCacheEntry); +const FastVector* GetUserImageInfos(); +const FastVector* GetKernelImageInfos(); // diff --git a/public/common/TracyQueue.hpp b/public/common/TracyQueue.hpp index 52f10baa99..e41fae056f 100644 --- a/public/common/TracyQueue.hpp +++ b/public/common/TracyQueue.hpp @@ -698,7 +698,7 @@ struct QueueHeader static constexpr size_t MaxModule = 1024; -struct QueuModuleInfo +struct QueueImageEntry { }; @@ -706,7 +706,7 @@ struct QueuModuleInfo enum struct PacketDataType : int { EMPTY = 0, - ModuleInfo, + ImageEntry, }; static_assert( PacketDataType::EMPTY == (PacketDataType)0, "Empty must be First" ); @@ -806,7 +806,7 @@ struct QueueItem QueueSourceCodeNotAvailable sourceCodeNotAvailable; QueueFiberEnter fiberEnter; QueueFiberLeave fiberLeave; - QueuModuleInfo moduleInfo; + QueueImageEntry imageEntry; QueueDataPacket packet; }; }; diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index b82efaef06..3371aefd58 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -1690,15 +1690,15 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow { for (size_t i = 0; i < moduleCount; i++) { - ModuleCacheEntry moduleEntry; - f.Read(moduleEntry.start); - f.Read(moduleEntry.end); - DeserializeMallocedFastString(&moduleEntry.name); - DeserializeMallocedFastString(&moduleEntry.path); + ImageEntry imageEntry; + f.Read(imageEntry.start); + f.Read(imageEntry.end); + DeserializeMallocedFastString(&imageEntry.name); + DeserializeMallocedFastString(&imageEntry.path); - DeserializeDebugField(&moduleEntry.degugModuleField); + DeserializeDebugField(&imageEntry.degugModuleField); - CacheModuleAndLoadExternal(moduleEntry); + CacheModuleAndLoadExternal(imageEntry); } } @@ -1708,20 +1708,20 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow { for (size_t i = 0; i < moduleCount; i++) { - ModuleCacheEntry driver; - f.Read(driver.start); - driver.end = 0; + ImageEntry kernelImageEntry; + f.Read(kernelImageEntry.start); + kernelImageEntry.end = 0; char* name = nullptr; DeserializeMallocedFastString(&name); - driver.name = name; + kernelImageEntry.name = name; char* path = nullptr; DeserializeMallocedFastString(&path); - driver.path = path; + kernelImageEntry.path = path; - DeserializeDebugField(&driver.degugModuleField); + DeserializeDebugField(&kernelImageEntry.degugModuleField); - CacheModuleKernelAndLoadExternal(driver); + CacheModuleAndLoadExternal(kernelImageEntry); } } @@ -4647,7 +4647,7 @@ bool Worker::Process( const QueueItem& ev ) switch( ev.hdr.type ) { case QueueType::ModuleUpdate: - DispatchModuleInfo( ev.moduleInfo ); + DispatchImageEntry( ev.imageEntry ); break; case QueueType::ThreadContext: ProcessThreadContext( ev.threadCtx ); @@ -8626,13 +8626,12 @@ void Worker::Write( FileWrite& f, bool fiDict ) f.Write(s, sLenght); }; - const FastVector& cacheModule = GetModuleData(); - uint32_t moduleCount = static_cast(cacheModule.size()); + const FastVector* cacheModule = GetUserImageInfos(); + uint32_t moduleCount = static_cast(cacheModule ? cacheModule->size() : 0); f.Write(&moduleCount, sizeof(moduleCount)); - for (const ModuleCacheEntry& it : cacheModule) + for (const ImageEntry& moduleCacheEntry : *cacheModule) { - const ModuleCacheEntry& moduleCacheEntry = it; f.Write(&moduleCacheEntry.start, sizeof(moduleCacheEntry.start)); f.Write(&moduleCacheEntry.end, sizeof(moduleCacheEntry.end)); @@ -8643,11 +8642,11 @@ void Worker::Write( FileWrite& f, bool fiDict ) } - const FastVector& kernelDrivers = GetKernelDriver(); - moduleCount = static_cast(kernelDrivers.size()); + const FastVector* kernelDrivers = GetKernelImageInfos(); + moduleCount = static_cast(kernelDrivers ? kernelDrivers->size() : 0); f.Write(&moduleCount, sizeof(moduleCount)); - for (const ModuleCacheEntry& it : kernelDrivers) + for (const ImageEntry& it : *kernelDrivers) { f.Write(&it.start, sizeof(it.start)); assert(it.end == 0 && "kernel end should be zero"); @@ -8906,7 +8905,7 @@ void Worker::CacheSourceFiles() } } -void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) +void Worker::DispatchImageEntry( const QueueImageEntry& ev ) { uint8_t* ptrToData = nullptr; size_t s = 0; @@ -8915,7 +8914,7 @@ void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) switch( dataType ) { - case PacketDataType::ModuleInfo: + case PacketDataType::ImageEntry: { uint64_t baseAddress = MemRead( ptrToData ); ptrToData += sizeof( baseAddress ); @@ -8945,7 +8944,7 @@ void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) DebugFormat debugFormat = MemRead( ptrToData ); ptrToData += sizeof( DebugFormat ); - ModuleCacheEntry moduleCacheEntry = + ImageEntry moduleCacheEntry = { .start = baseAddress, .end = end, @@ -8954,7 +8953,7 @@ void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) }; moduleCacheEntry.degugModuleField.debugFormat = debugFormat; - if (debugFormat == DebugFormat::PdbDebugFormat) + if (debugFormat != DebugFormat::NoDebugFormat) { uint32_t debugFormatSize = MemRead( ptrToData ); @@ -8968,8 +8967,6 @@ void Worker::DispatchModuleInfo( const QueuModuleInfo& ev ) degugModuleField.debugData = debugData; degugModuleField.debugDataSize = debugFormatSize; - - assert((char)(debugData[debugFormatSize - 1]) == '\0' && "missing end of string"); } diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 7233b4abcd..1a18a1df51 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -793,7 +793,7 @@ class Worker tracy_force_inline void ProcessFiberEnter( const QueueFiberEnter& ev ); tracy_force_inline void ProcessFiberLeave( const QueueFiberLeave& ev ); - tracy_force_inline void DispatchModuleInfo( const QueuModuleInfo& ev); + tracy_force_inline void DispatchImageEntry( const QueueImageEntry& ev); tracy_force_inline ZoneEvent* AllocZoneEvent(); tracy_force_inline void ProcessZoneBeginImpl( ZoneEvent* zone, const QueueZoneBegin& ev ); From 361618b9d383825ed8aa8fec44a85e6d51374a93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sun, 9 Mar 2025 17:55:40 +0100 Subject: [PATCH 10/40] Symbol resolution configuration + update versions --- capture/src/capture.cpp | 5 +- csvexport/src/csvexport.cpp | 4 +- import/src/import-chrome.cpp | 4 +- import/src/import-fuchsia.cpp | 6 +- profiler/src/main.cpp | 18 ++++ profiler/src/profiler/TracyConfig.hpp | 2 + profiler/src/profiler/TracyView.cpp | 5 +- profiler/src/profiler/TracyView_Compare.cpp | 4 +- public/client/TracyProfiler.cpp | 9 ++ public/common/TracyCallstack.cpp | 62 ++++++------- public/common/TracyCallstack.hpp | 2 + public/common/TracyProtocol.hpp | 10 ++- public/common/TracyVersion.hpp | 2 +- server/TracyWorker.cpp | 98 ++++++++++++--------- server/TracyWorker.hpp | 13 ++- update/src/update.cpp | 6 +- 16 files changed, 160 insertions(+), 90 deletions(-) diff --git a/capture/src/capture.cpp b/capture/src/capture.cpp index ff833f739a..bb4f3eb1c8 100644 --- a/capture/src/capture.cpp +++ b/capture/src/capture.cpp @@ -164,7 +164,10 @@ int main( int argc, char** argv ) printf( "Connecting to %s:%i...", address, port ); fflush( stdout ); - tracy::Worker worker( address, port, memoryLimit ); + + tracy::Worker::SymbolResolutionConfig symConfig{}; + symConfig.m_attemptResolutionByWorker = false; + tracy::Worker worker( address, port, memoryLimit, symConfig ); while( !worker.HasData() ) { const auto handshake = worker.GetHandshakeStatus(); diff --git a/csvexport/src/csvexport.cpp b/csvexport/src/csvexport.cpp index d721f0c08f..ef7da9ccb9 100644 --- a/csvexport/src/csvexport.cpp +++ b/csvexport/src/csvexport.cpp @@ -214,7 +214,9 @@ int main(int argc, char** argv) return 1; } - auto worker = tracy::Worker(*f); + tracy::Worker::SymbolResolutionConfig symConfig{}; + symConfig.m_attemptResolutionByWorker = false; + auto worker = tracy::Worker(*f, symConfig); if (args.unwrapMessages) { diff --git a/import/src/import-chrome.cpp b/import/src/import-chrome.cpp index 936d37d936..30d6d7c843 100644 --- a/import/src/import-chrome.cpp +++ b/import/src/import-chrome.cpp @@ -346,7 +346,9 @@ int main( int argc, char** argv ) return out; }; - tracy::Worker worker( getFilename(output), getFilename(input), timeline, messages, plots, threadNames ); + tracy::Worker::SymbolResolutionConfig symConfig{}; + symConfig.m_attemptResolutionByWorker = false; + tracy::Worker worker( getFilename(output), getFilename(input), timeline, messages, plots, threadNames, symConfig ); auto w = std::unique_ptr( tracy::FileWrite::Open( output, clev ) ); if( !w ) diff --git a/import/src/import-fuchsia.cpp b/import/src/import-fuchsia.cpp index fb4debbf12..ddb0218011 100644 --- a/import/src/import-fuchsia.cpp +++ b/import/src/import-fuchsia.cpp @@ -665,9 +665,11 @@ int main(int argc, char **argv) { out--; return out; }; - + + tracy::Worker::SymbolResolutionConfig symConfig{}; + symConfig.m_attemptResolutionByWorker = false; tracy::Worker worker(getFilename(output), getFilename(input), timeline, - messages, plots, std::move(dec.threadNames)); + messages, plots, std::move(dec.threadNames), symConfig); auto w = std::unique_ptr(tracy::FileWrite::Open(output, clev)); diff --git a/profiler/src/main.cpp b/profiler/src/main.cpp index 7cb4c09de6..c897d4ca98 100644 --- a/profiler/src/main.cpp +++ b/profiler/src/main.cpp @@ -237,6 +237,8 @@ static void LoadConfig() if( ini_sget( ini, "memory", "percent", "%d", &v ) && v >= 1 && v < 1000 ) s_config.memoryLimitPercent = v; if( ini_sget( ini, "achievements", "enabled", "%d", &v ) ) s_config.achievements = v; if( ini_sget( ini, "achievements", "asked", "%d", &v ) ) s_config.achievementsAsked = v; + if( ini_sget( ini, "symbols", "attemptResolutionByServer", "%d", &v ) ) s_config.symbolsAttemptResolutionByServer = v; + if( ini_sget( ini, "symbols", "preventResolutionByClient", "%d", &v ) ) s_config.symbolsPreventResolutionByClient = v; ini_free( ini ); } @@ -267,6 +269,10 @@ static bool SaveConfig() fprintf( f, "enabled = %i\n", (int)s_config.achievements ); fprintf( f, "asked = %i\n", (int)s_config.achievementsAsked ); + fprintf(f, "\n[symbols]\n"); + fprintf(f, "attemptResolutionByServer = %i\n", (int)s_config.symbolsAttemptResolutionByServer); + fprintf(f, "preventResolutionByClient = %i\n", (int)s_config.symbolsPreventResolutionByClient); + fclose( f ); return true; } @@ -840,6 +846,18 @@ static void DrawContents() ImGui::Spacing(); if( ImGui::Checkbox( "Enable achievements", &s_config.achievements ) ) SaveConfig(); + ImGui::TextUnformatted("Symbol resolution"); + ImGui::Indent(); + if (ImGui::Checkbox("Attempt resolution by profiler", &s_config.symbolsAttemptResolutionByServer)) SaveConfig(); + ImGui::SameLine(); + tracy::DrawHelpMarker("When enabled, the profiler will attempt to resolve symbols first before querying the application."); + + if (ImGui::Checkbox("Prevent resolution by application", &s_config.symbolsPreventResolutionByClient)) SaveConfig(); + ImGui::SameLine(); + tracy::DrawHelpMarker("When enabled, the application will not attempt to resolve symbols at all. The profiler can do the resolution, or it will require to use the `update` tool."); + + ImGui::Unindent(); + ImGui::PopStyleVar(); ImGui::TreePop(); } diff --git a/profiler/src/profiler/TracyConfig.hpp b/profiler/src/profiler/TracyConfig.hpp index bd0c464e67..1df4d132b6 100644 --- a/profiler/src/profiler/TracyConfig.hpp +++ b/profiler/src/profiler/TracyConfig.hpp @@ -20,6 +20,8 @@ struct Config int dynamicColors = 1; bool forceColors = false; int shortenName = (int)ShortenName::NoSpaceAndNormalize; + bool symbolsAttemptResolutionByServer = true; + bool symbolsPreventResolutionByClient = false; }; } diff --git a/profiler/src/profiler/TracyView.cpp b/profiler/src/profiler/TracyView.cpp index 1668eda860..92ce19aaf4 100644 --- a/profiler/src/profiler/TracyView.cpp +++ b/profiler/src/profiler/TracyView.cpp @@ -36,7 +36,7 @@ namespace tracy double s_time = 0; View::View( void(*cbMainThread)(const std::function&, bool), const char* addr, uint16_t port, ImFont* fixedWidth, ImFont* smallFont, ImFont* bigFont, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, const Config& config, AchievementsMgr* amgr ) - : m_worker( addr, port, config.memoryLimit == 0 ? -1 : ( config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100 ) ) + : m_worker( addr, port, config.memoryLimit == 0 ? -1 : ( config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100), Worker::SymbolResolutionConfig{ config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient }) , m_staticView( false ) , m_viewMode( ViewMode::LastFrames ) , m_viewModeHeuristicTry( true ) @@ -66,10 +66,11 @@ View::View( void(*cbMainThread)(const std::function&, bool), const char* { InitTextEditor(); SetupConfig( config ); + } View::View( void(*cbMainThread)(const std::function&, bool), FileRead& f, ImFont* fixedWidth, ImFont* smallFont, ImFont* bigFont, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, const Config& config, AchievementsMgr* amgr ) - : m_worker( f ) + : m_worker( f, { config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient }, EventType::All, true, false) , m_filename( f.GetFilename() ) , m_staticView( true ) , m_viewMode( ViewMode::Paused ) diff --git a/profiler/src/profiler/TracyView_Compare.cpp b/profiler/src/profiler/TracyView_Compare.cpp index 82a5bc1daa..f77a9db88b 100644 --- a/profiler/src/profiler/TracyView_Compare.cpp +++ b/profiler/src/profiler/TracyView_Compare.cpp @@ -255,7 +255,9 @@ void View::DrawCompare() m_compare.loadThread = std::thread( [this, f] { try { - m_compare.second = std::make_unique( *f, EventType::SourceCache ); + tracy::Worker::SymbolResolutionConfig symConfig{}; + symConfig.m_attemptResolutionByWorker = false; + m_compare.second = std::make_unique( *f, symConfig, EventType::SourceCache ); m_compare.userData = std::make_unique( m_compare.second->GetCaptureProgram().c_str(), m_compare.second->GetCaptureTime() ); m_compare.diffDirection = m_worker.GetCaptureTime() < m_compare.second->GetCaptureTime(); } diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 4ff46d0198..652a1fee9b 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -1942,6 +1942,15 @@ void Profiler::Worker() LZ4_resetStream( (LZ4_stream_t*)m_stream ); m_sock->Send( &welcome, sizeof( welcome ) ); + uint32_t serverFlags = 0; + if (m_sock->ReadRaw(&serverFlags, sizeof(serverFlags), 2000)) + { + if (serverFlags & ServerFlags::PreventSymbolResolution) + { + PreventSymbolResolution(); + } + } + m_threadCtx = 0; m_refTimeSerial = 0; m_refTimeCtx = 0; diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 319c6e87bc..f4bfde2701 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -129,17 +129,11 @@ namespace tracy // 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; + static bool s_shouldResolveSymbolsOffline = true; #else static bool s_shouldResolveSymbolsOffline = false; - - bool ShouldResolveSymbolsOffline() - { - return GetEnvBool("TRACY_SYMBOL_OFFLINE_RESOLVE", false); - } #endif // TRACY_SYMBOL_OFFLINE_RESOLVE - - bool s_serverLocalResolve = true; // Should the profiler try to resolve symbols before querying the client for symbols. + void PreventSymbolResolution() { s_shouldResolveSymbolsOffline = true; } inline bool IsKernelAddress(uint64_t addr) { return (addr >> 63) != 0; @@ -639,7 +633,7 @@ ModuleNameAndBaseAddress OnFailedFindAddress(uint64_t address) } } } - return ModuleNameAndBaseAddress{ "[unknown]", address }; + return {}; } ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols(uint64_t addr, bool* failed) @@ -663,13 +657,8 @@ ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols(uint64_t addr, bool* fai return ModuleNameAndBaseAddress{ entry->name, entry->start }; - if (s_serverLocalResolve) - { - *failed = true; - return ModuleNameAndBaseAddress{ "[unknown]", addr }; - } - - return OnFailedFindAddress(addr); + *failed = true; + return ModuleNameAndBaseAddress{ "[unknown]", addr }; } @@ -815,11 +804,8 @@ static void CacheProcessModules() void InitCallstack() { - s_serverLocalResolve = GetEnvBool("SERVER_LOCAL_RESOLVE", true); - - #ifndef TRACY_SYMBOL_OFFLINE_RESOLVE - s_shouldResolveSymbolsOffline = ShouldResolveSymbolsOffline(); + s_shouldResolveSymbolsOffline = GetEnvBool("TRACY_SYMBOL_OFFLINE_RESOLVE", s_shouldResolveSymbolsOffline); #endif //#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE if (s_shouldResolveSymbolsOffline) { @@ -985,13 +971,6 @@ bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, // Called from the profiler (server) only, we received data from the client. void CacheModuleAndLoadExternal(ImageEntry& imageEntry) { - - if (!s_serverLocalResolve) - { - DestroyImageEntry(imageEntry); - return; - } - if (IsKernelAddress(imageEntry.start)) { s_krnlCache->CacheModuleWithDebugInfo(imageEntry); @@ -1075,16 +1054,31 @@ CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _d InitRpmalloc(); - bool reseachFailed = false; - const ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols(ptr, &reseachFailed); + bool moduleNotFound = false; + ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols(ptr, &moduleNotFound); - - if (reseachFailed) + if (moduleNotFound) { - *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::ModuleMissing; + if (s_DbgHelpSymHandle == GetCurrentProcess()) + { + // We're on the client or self profiling, try to load a potentially new module. + moduleNameAndAddress = OnFailedFindAddress(ptr); + if (moduleNameAndAddress.baseAddr == 0) + { + // Failed to find the module information. + // Set base address to ptr so that cb_data[0].symAddr=0 + moduleNameAndAddress = { "[unknown]", ptr }; + } + else moduleNotFound = false; // We managed to load the module information + } + else + { + // We're on the server, it does not have a way to get information about the module + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::ModuleMissing; + } } - if (s_shouldResolveSymbolsOffline || reseachFailed) + if (s_shouldResolveSymbolsOffline || moduleNotFound) { #ifdef TRACY_DBGHELP_LOCK DBGHELP_UNLOCK; @@ -1415,7 +1409,7 @@ void InitCallstack() #endif //#ifdef TRACY_USE_IMAGE_CACHE #ifndef TRACY_SYMBOL_OFFLINE_RESOLVE - s_shouldResolveSymbolsOffline = ShouldResolveSymbolsOffline(); + s_shouldResolveSymbolsOffline = GetEnvBool("TRACY_SYMBOL_OFFLINE_RESOLVE", s_shouldResolveSymbolsOffline); #endif //#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE if( s_shouldResolveSymbolsOffline ) { diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index 52e5b05670..0289df7ddc 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -11,6 +11,7 @@ namespace tracy { static constexpr bool has_callstack() { return false; } static tracy_force_inline void* Callstack( int32_t /*depth*/ ) { return nullptr; } +void PreventSymbolResolution() { } } #else @@ -96,6 +97,7 @@ struct ImageEntry }; +void PreventSymbolResolution(); std::recursive_mutex& GetModuleCacheMutexForRead(); CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ); diff --git a/public/common/TracyProtocol.hpp b/public/common/TracyProtocol.hpp index 85367de49c..0629f8c364 100644 --- a/public/common/TracyProtocol.hpp +++ b/public/common/TracyProtocol.hpp @@ -9,7 +9,7 @@ namespace tracy constexpr unsigned Lz4CompressBound( unsigned isize ) { return isize + ( isize / 255 ) + 16; } -enum : uint32_t { ProtocolVersion = 72 }; +enum : uint32_t { ProtocolVersion = 73 }; enum : uint16_t { BroadcastVersion = 3 }; using lz4sz_t = uint32_t; @@ -22,6 +22,7 @@ static_assert( TargetFrameSize * 2 >= 64 * 1024, "Not enough space for LZ4 strea enum { HandshakeShibbolethSize = 8 }; static const char HandshakeShibboleth[HandshakeShibbolethSize] = { 'T', 'r', 'a', 'c', 'y', 'P', 'r', 'f' }; + enum HandshakeStatus : uint8_t { HandshakePending, @@ -112,6 +113,13 @@ struct WelcomeMessage enum { WelcomeMessageSize = sizeof( WelcomeMessage ) }; +struct ServerFlags +{ + enum _t : uint32_t + { + PreventSymbolResolution = 1 << 0, + }; +}; struct OnDemandPayloadMessage { diff --git a/public/common/TracyVersion.hpp b/public/common/TracyVersion.hpp index 12642d652b..5cab83c9fe 100644 --- a/public/common/TracyVersion.hpp +++ b/public/common/TracyVersion.hpp @@ -7,7 +7,7 @@ namespace Version { enum { Major = 0 }; enum { Minor = 11 }; -enum { Patch = 2 }; +enum { Patch = 3 }; } } diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 3371aefd58..0e223131b4 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -265,7 +265,7 @@ static bool IsQueryPrio( ServerQuery type ) LoadProgress Worker::s_loadProgress; -Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit ) +Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const SymbolResolutionConfig& symbolResConfig) : m_addr( addr ) , m_port( port ) , m_hasData( false ) @@ -273,6 +273,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit ) , m_buffer( new char[TargetFrameSize*3 + 1] ) , m_bufferOffset( 0 ) , m_inconsistentSamples( false ) + , m_symbolConfig(symbolResConfig) , m_pendingStrings( 0 ) , m_pendingThreads( 0 ) , m_pendingFibers( 0 ) @@ -309,7 +310,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit ) m_threadNet = std::thread( [this] { SetThreadName( "Tracy Network" ); Network(); } ); } -Worker::Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames ) +Worker::Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames, const SymbolResolutionConfig& symbolResConfig) : m_hasData( true ) , m_delay( 0 ) , m_resolution( 0 ) @@ -323,6 +324,7 @@ Worker::Worker( const char* name, const char* program, const std::vector= FileVersion( 0, 11, 3 ) ) { + uint32_t moduleCount = -1; + f.Read(moduleCount); for (size_t i = 0; i < moduleCount; i++) { ImageEntry imageEntry; @@ -1698,14 +1700,11 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow DeserializeDebugField(&imageEntry.degugModuleField); - CacheModuleAndLoadExternal(imageEntry); + if (m_symbolConfig.m_attemptResolutionByWorker) + CacheModuleAndLoadExternal(imageEntry); } - } - - f.Read(moduleCount); - if (moduleCount != -1) - { + f.Read(moduleCount); for (size_t i = 0; i < moduleCount; i++) { ImageEntry kernelImageEntry; @@ -1721,7 +1720,8 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow DeserializeDebugField(&kernelImageEntry.degugModuleField); - CacheModuleAndLoadExternal(kernelImageEntry); + if (m_symbolConfig.m_attemptResolutionByWorker) + CacheModuleAndLoadExternal(kernelImageEntry); } } @@ -2794,6 +2794,7 @@ void Worker::Exec() m_sock.Send( HandshakeShibboleth, HandshakeShibbolethSize ); uint32_t protocolVersion = ProtocolVersion; m_sock.Send( &protocolVersion, sizeof( protocolVersion ) ); + HandshakeStatus handshake; if( !m_sock.Read( &handshake, sizeof( handshake ), 10, ShouldExit ) ) { @@ -2868,6 +2869,16 @@ void Worker::Exec() m_hostInfo = welcome.hostInfo; + uint32_t serverFlags = 0; + if (m_symbolConfig.m_preventResolutionByClient) + { + // We could probably enable this automatically on windows when connected to localhost + // or based on some platform filter? For now, simply honor the config. + serverFlags |= ServerFlags::PreventSymbolResolution; + } + m_sock.Send(&serverFlags, sizeof(serverFlags)); + + if( m_onDemand ) { OnDemandPayloadMessage onDemand; @@ -4086,9 +4097,6 @@ uint64_t Worker::GetCanonicalPointer( const CallstackFrameId& id ) const return ( id.idx & 0x3FFFFFFFFFFFFFFF ) | ( ( id.idx & 0x3000000000000000 ) << 2 ); } -// TODO: handle properly -extern bool s_serverLocalResolve; - void Worker::TryResolveCallStackIfNeeded(CallstackFrameId frameId) { // If we already have the information @@ -4100,7 +4108,7 @@ void Worker::TryResolveCallStackIfNeeded(CallstackFrameId frameId) uint64_t symbolAddress = GetCanonicalPointer(frameId); DecodeCallStackPtrStatus status = DecodeCallStackPtrStatus::Count; - if (s_serverLocalResolve) + if (m_symbolConfig.m_attemptResolutionByWorker) { // TODO: offload to a worker thread CallstackEntryData outCallStack = DecodeCallstackPtr(symbolAddress, &status); @@ -8626,36 +8634,41 @@ void Worker::Write( FileWrite& f, bool fiDict ) f.Write(s, sLenght); }; - const FastVector* cacheModule = GetUserImageInfos(); - uint32_t moduleCount = static_cast(cacheModule ? cacheModule->size() : 0); + const FastVector* userlandImages = GetUserImageInfos(); + uint32_t moduleCount = static_cast(userlandImages ? userlandImages->size() : 0); f.Write(&moduleCount, sizeof(moduleCount)); + + if (userlandImages) + { + for (const ImageEntry& moduleCacheEntry : *userlandImages) + { + f.Write(&moduleCacheEntry.start, sizeof(moduleCacheEntry.start)); + f.Write(&moduleCacheEntry.end, sizeof(moduleCacheEntry.end)); - for (const ImageEntry& moduleCacheEntry : *cacheModule) - { - f.Write(&moduleCacheEntry.start, sizeof(moduleCacheEntry.start)); - f.Write(&moduleCacheEntry.end, sizeof(moduleCacheEntry.end)); - - SerializeString(moduleCacheEntry.name); - SerializeString(moduleCacheEntry.path); - - SerializeDebugModuleField(moduleCacheEntry.degugModuleField); - } + SerializeString(moduleCacheEntry.name); + SerializeString(moduleCacheEntry.path); + SerializeDebugModuleField(moduleCacheEntry.degugModuleField); + } + } - const FastVector* kernelDrivers = GetKernelImageInfos(); - moduleCount = static_cast(kernelDrivers ? kernelDrivers->size() : 0); + const FastVector* kernelImages = GetKernelImageInfos(); + moduleCount = static_cast(kernelImages ? kernelImages->size() : 0); f.Write(&moduleCount, sizeof(moduleCount)); + + if (kernelImages) + { + for (const ImageEntry& it : *kernelImages) + { + f.Write(&it.start, sizeof(it.start)); + assert(it.end == 0 && "kernel end should be zero"); - for (const ImageEntry& it : *kernelDrivers) - { - f.Write(&it.start, sizeof(it.start)); - assert(it.end == 0 && "kernel end should be zero"); - - SerializeString(it.name); - SerializeString(it.path); + SerializeString(it.name); + SerializeString(it.path); - SerializeDebugModuleField(it.degugModuleField); - } + SerializeDebugModuleField(it.degugModuleField); + } + } } @@ -8970,7 +8983,8 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) } - CacheModuleAndLoadExternal(moduleCacheEntry); + if (m_symbolConfig.m_attemptResolutionByWorker) + CacheModuleAndLoadExternal(moduleCacheEntry); break; } diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 1a18a1df51..dae63f0155 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -462,9 +462,15 @@ class Worker NUM_FAILURES }; - Worker( const char* addr, uint16_t port, int64_t memoryLimit ); - Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames ); - Worker( FileRead& f, EventType::Type eventMask = EventType::All, bool bgTasks = true, bool allowStringModification = false); + struct SymbolResolutionConfig + { + bool m_attemptResolutionByWorker; + bool m_preventResolutionByClient; + }; + + Worker(const char* addr, uint16_t port, int64_t memoryLimit, const SymbolResolutionConfig& symbolResConfig); + Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames, const SymbolResolutionConfig& symbolResConfig); + Worker(FileRead& f, const SymbolResolutionConfig& symbolResConfig, EventType::Type eventMask = EventType::All, bool bgTasks = true, bool allowStringModification = false); ~Worker(); const std::string& GetAddr() const { return m_addr; } @@ -1029,6 +1035,7 @@ class Worker bool m_identifySamples = false; bool m_inconsistentSamples; bool m_allowStringModification = false; + SymbolResolutionConfig m_symbolConfig; short_ptr m_gpuCtxMap[256]; uint32_t m_pendingCallstackId = 0; diff --git a/update/src/update.cpp b/update/src/update.cpp index a9cb625edd..0e29e976dd 100644 --- a/update/src/update.cpp +++ b/update/src/update.cpp @@ -170,7 +170,11 @@ int main( int argc, char** argv ) const auto t0 = std::chrono::high_resolution_clock::now(); const bool allowBgThreads = false; const bool allowStringModification = resolveSymbols; - tracy::Worker worker( *f, (tracy::EventType::Type)events, allowBgThreads, allowStringModification ); + tracy::Worker::SymbolResolutionConfig symConfig{}; + // TODO: Use the same mechanism everywhere + // Current offline resolution does not check if symbols match which can lead to invalid data. + symConfig.m_attemptResolutionByWorker = false; // resolveSymbols; + tracy::Worker worker( *f, symConfig, (tracy::EventType::Type)events, allowBgThreads, allowStringModification ); #ifndef TRACY_NO_STATISTICS while( !worker.AreSourceLocationZonesReady() ) std::this_thread::sleep_for( std::chrono::milliseconds( 10 ) ); From e51f755a52659a024f6113a510a3f546fa6e3c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sun, 9 Mar 2025 18:46:11 +0100 Subject: [PATCH 11/40] Clean formating and remove unneeded std usage --- profiler/src/main.cpp | 10 +- profiler/src/profiler/TracySourceView.cpp | 2 +- profiler/src/profiler/TracyView.cpp | 4 +- public/client/TracyProfiler.cpp | 98 ++-- public/client/TracyProfiler.hpp | 6 +- public/common/TracyCallstack.cpp | 433 ++++++++---------- public/common/TracyCallstack.hpp | 20 +- public/common/TracyDebugModulesHeaderFile.hpp | 26 +- public/common/TracyFastVector.hpp | 4 - public/tracy/Tracy.hpp | 2 +- server/TracyWorker.cpp | 363 +++++++-------- server/TracyWorker.hpp | 11 +- 12 files changed, 441 insertions(+), 538 deletions(-) diff --git a/profiler/src/main.cpp b/profiler/src/main.cpp index c897d4ca98..d03391cc0a 100644 --- a/profiler/src/main.cpp +++ b/profiler/src/main.cpp @@ -846,15 +846,15 @@ static void DrawContents() ImGui::Spacing(); if( ImGui::Checkbox( "Enable achievements", &s_config.achievements ) ) SaveConfig(); - ImGui::TextUnformatted("Symbol resolution"); + ImGui::TextUnformatted( "Symbol resolution" ); ImGui::Indent(); - if (ImGui::Checkbox("Attempt resolution by profiler", &s_config.symbolsAttemptResolutionByServer)) SaveConfig(); + if( ImGui::Checkbox( "Attempt resolution by profiler", &s_config.symbolsAttemptResolutionByServer ) ) SaveConfig(); ImGui::SameLine(); - tracy::DrawHelpMarker("When enabled, the profiler will attempt to resolve symbols first before querying the application."); + tracy::DrawHelpMarker( "When enabled, the profiler will attempt to resolve symbols first before querying the application." ); - if (ImGui::Checkbox("Prevent resolution by application", &s_config.symbolsPreventResolutionByClient)) SaveConfig(); + if( ImGui::Checkbox( "Prevent resolution by application", &s_config.symbolsPreventResolutionByClient ) ) SaveConfig(); ImGui::SameLine(); - tracy::DrawHelpMarker("When enabled, the application will not attempt to resolve symbols at all. The profiler can do the resolution, or it will require to use the `update` tool."); + tracy::DrawHelpMarker( "When enabled, the application will not attempt to resolve symbols at all. The profiler can do the resolution, or it will require to use the `update` tool." ); ImGui::Unindent(); diff --git a/profiler/src/profiler/TracySourceView.cpp b/profiler/src/profiler/TracySourceView.cpp index 10a9eb63ab..f68697822a 100644 --- a/profiler/src/profiler/TracySourceView.cpp +++ b/profiler/src/profiler/TracySourceView.cpp @@ -1157,7 +1157,7 @@ void SourceView::RenderSymbolView( Worker& worker, View& view ) const auto shortenName = view.GetShortenName(); auto sym = worker.GetSymbolData( m_symAddr ); - if (sym == nullptr) return; // Might no have received symbol info yet + if( sym == nullptr ) return; // Might no have received symbol info yet ImGui::PushFont( m_bigFont ); ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) ); diff --git a/profiler/src/profiler/TracyView.cpp b/profiler/src/profiler/TracyView.cpp index 92ce19aaf4..ad3de94557 100644 --- a/profiler/src/profiler/TracyView.cpp +++ b/profiler/src/profiler/TracyView.cpp @@ -36,7 +36,7 @@ namespace tracy double s_time = 0; View::View( void(*cbMainThread)(const std::function&, bool), const char* addr, uint16_t port, ImFont* fixedWidth, ImFont* smallFont, ImFont* bigFont, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, const Config& config, AchievementsMgr* amgr ) - : m_worker( addr, port, config.memoryLimit == 0 ? -1 : ( config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100), Worker::SymbolResolutionConfig{ config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient }) + : m_worker( addr, port, config.memoryLimit == 0 ? -1 : ( config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100 ), Worker::SymbolResolutionConfig{ config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient } ) , m_staticView( false ) , m_viewMode( ViewMode::LastFrames ) , m_viewModeHeuristicTry( true ) @@ -70,7 +70,7 @@ View::View( void(*cbMainThread)(const std::function&, bool), const char* } View::View( void(*cbMainThread)(const std::function&, bool), FileRead& f, ImFont* fixedWidth, ImFont* smallFont, ImFont* bigFont, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, const Config& config, AchievementsMgr* amgr ) - : m_worker( f, { config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient }, EventType::All, true, false) + : m_worker( f, { config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient }, EventType::All, true, false ) , m_filename( f.GetFilename() ) , m_staticView( true ) , m_viewMode( ViewMode::Paused ) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 652a1fee9b..f8d4d56188 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -1943,9 +1943,9 @@ void Profiler::Worker() m_sock->Send( &welcome, sizeof( welcome ) ); uint32_t serverFlags = 0; - if (m_sock->ReadRaw(&serverFlags, sizeof(serverFlags), 2000)) + if( m_sock->ReadRaw( &serverFlags, sizeof( serverFlags ), 2000 ) ) { - if (serverFlags & ServerFlags::PreventSymbolResolution) + if( serverFlags & ServerFlags::PreventSymbolResolution ) { PreventSymbolResolution(); } @@ -4084,20 +4084,20 @@ void Profiler::SendModulesCaches() std::lock_guard mutexguard{ GetModuleCacheMutexForRead() }; const FastVector* kernelDrivers = GetKernelImageInfos(); - if(kernelDrivers) + if( kernelDrivers ) { - for (auto& it : *kernelDrivers) + for ( auto& it : *kernelDrivers ) { - SendModuleInfo(it); + SendModuleInfo( it ); } } const FastVector* moduleCache = GetUserImageInfos(); if(moduleCache) { - for (auto& it : *moduleCache) + for ( auto& it : *moduleCache ) { - SendModuleInfo(it); + SendModuleInfo( it ); } } #endif @@ -4105,89 +4105,89 @@ void Profiler::SendModulesCaches() #ifdef TRACY_HAS_CALLSTACK -void Profiler::WriteDebugFieldToPacket(uint8_t** ptr, int* currentPacketSize, const DegugModuleField& degugModuleField) +void Profiler::WriteDebugFieldToPacket( uint8_t** ptr, int* currentPacketSize, const ImageDebugInfo& imageDebugInfo ) { - MemWrite(*ptr, degugModuleField.debugFormat); - *ptr += sizeof(degugModuleField.debugFormat); - *currentPacketSize += sizeof(degugModuleField.debugFormat); + MemWrite( *ptr, imageDebugInfo.debugFormat ); + *ptr += sizeof( imageDebugInfo.debugFormat ); + *currentPacketSize += sizeof( imageDebugInfo.debugFormat ); - const uint32_t dataSize = static_cast(degugModuleField.debugDataSize); + const uint32_t dataSize = static_cast( imageDebugInfo.debugDataSize ); - MemWrite(*ptr, dataSize); - *ptr += sizeof(dataSize); - *currentPacketSize += sizeof(dataSize); + MemWrite( *ptr, dataSize ); + *ptr += sizeof( dataSize ); + *currentPacketSize += sizeof( dataSize ); - memcpy(*ptr, degugModuleField.debugData, dataSize); + memcpy( *ptr, imageDebugInfo.debugData, dataSize ); *ptr += dataSize; *currentPacketSize += dataSize; } -void Profiler::SendModuleInfo(const ImageEntry& imageEntry) +void Profiler::SendModuleInfo( const ImageEntry& imageEntry ) { static constexpr int EndOfString = 1; - const uint32_t moduleNameLength = (imageEntry.name ? strlen(imageEntry.name) : 0) + EndOfString; - const uint32_t modulePathLength = (imageEntry.path ? strlen(imageEntry.path) : 0) + EndOfString; + const uint32_t moduleNameLength = ( imageEntry.name ? strlen( imageEntry.name ) : 0 ) + EndOfString; + const uint32_t modulePathLength = ( imageEntry.path ? strlen( imageEntry.path ) : 0 ) + EndOfString; QueueItem item; - tracy::MemWrite(&item.hdr.type, (int)QueueType::DataPacket); - const size_t baseModuleInfo = sizeof(imageEntry.start) + sizeof(imageEntry.end) + - sizeof(moduleNameLength) + moduleNameLength + - sizeof(modulePathLength) + modulePathLength + - sizeof(imageEntry.degugModuleField.debugFormat); + tracy::MemWrite( &item.hdr.type, (int)QueueType::DataPacket ); + const size_t baseModuleInfo = sizeof( imageEntry.start ) + sizeof( imageEntry.end ) + + sizeof( moduleNameLength ) + moduleNameLength + + sizeof( modulePathLength ) + modulePathLength + + sizeof( imageEntry.imageDebugInfo.debugFormat ); - const uint32_t debugFormatSize = 0 + sizeof(uint32_t) // DebugDataSize - + static_cast(imageEntry.degugModuleField.debugDataSize); + const uint32_t debugFormatSize = 0 + sizeof( uint32_t ) // DebugDataSize + + static_cast( imageEntry.imageDebugInfo.debugDataSize ); const size_t bufferSize = baseModuleInfo + debugFormatSize; - void* queueBuffer = tracy_malloc(bufferSize); + void* queueBuffer = tracy_malloc( bufferSize ); int size = 0; uint8_t* ptr = (uint8_t*)queueBuffer; // baseModuleInfo - MemWrite(ptr, imageEntry.start); - ptr += sizeof(imageEntry.start); - size += sizeof(imageEntry.start); + MemWrite( ptr, imageEntry.start ); + ptr += sizeof( imageEntry.start ); + size += sizeof( imageEntry.start ); - MemWrite(ptr, imageEntry.end); - ptr += sizeof(imageEntry.end); - size += sizeof(imageEntry.end); + MemWrite( ptr, imageEntry.end ); + ptr += sizeof( imageEntry.end ); + size += sizeof( imageEntry.end ); - MemWrite(ptr, moduleNameLength); - ptr += sizeof(moduleNameLength); - size += sizeof(moduleNameLength); + MemWrite( ptr, moduleNameLength ); + ptr += sizeof( moduleNameLength ); + size += sizeof( moduleNameLength ); - memcpy(ptr, imageEntry.name ? imageEntry.name : "", moduleNameLength); + memcpy( ptr, imageEntry.name ? imageEntry.name : "", moduleNameLength ); ptr += moduleNameLength; size += moduleNameLength; - MemWrite(ptr, modulePathLength); - ptr += sizeof(modulePathLength); - size += sizeof(modulePathLength); + MemWrite( ptr, modulePathLength ); + ptr += sizeof( modulePathLength ); + size += sizeof( modulePathLength ); - memcpy(ptr, imageEntry.path ? imageEntry.path : "", modulePathLength); + memcpy( ptr, imageEntry.path ? imageEntry.path : "", modulePathLength ); ptr += modulePathLength; size += modulePathLength; - WriteDebugFieldToPacket(&ptr, &size, imageEntry.degugModuleField); + WriteDebugFieldToPacket( &ptr, &size, imageEntry.imageDebugInfo ); - assert(size == bufferSize); + assert( size == bufferSize ); - SendSingleDataPacket(queueBuffer, bufferSize, PacketDataType::ImageEntry); + SendSingleDataPacket( queueBuffer, bufferSize, PacketDataType::ImageEntry ); QueueItem item2; - tracy::MemWrite(&item2.hdr.type, (int)QueueType::ModuleUpdate); + tracy::MemWrite( &item2.hdr.type, (int)QueueType::ModuleUpdate ); - static_assert(tracy::QueueDataSize[(int)QueueType::ModuleUpdate] == sizeof(QueueItem), "Size missmatch"); + static_assert( tracy::QueueDataSize[(int)QueueType::ModuleUpdate] == sizeof( QueueItem ), "Size missmatch" ); - NeedDataSize(sizeof(item2)); - AppendData(&item2, sizeof(item2)); + NeedDataSize( sizeof( item2 ) ); + AppendData( &item2, sizeof( item2 ) ); - tracy_free(queueBuffer); + tracy_free( queueBuffer ); } #endif diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index 579372c488..f5fd1c955e 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -777,7 +777,7 @@ class Profiler void SendSecondString( const char* ptr ) { SendSecondString( ptr, strlen( ptr ) ); } void SendSecondString( const char* ptr, size_t len ); - void SendSingleDataPacket(void* ptr, size_t totalSize, PacketDataType type); + void SendSingleDataPacket( void* ptr, size_t totalSize, PacketDataType type ); // Allocated source location data layout: // 2b payload size @@ -922,8 +922,8 @@ class Profiler void SendModulesCaches(); #ifdef TRACY_HAS_CALLSTACK - void WriteDebugFieldToPacket(uint8_t** ptr, int* currentPacketSize, const DegugModuleField& degugModuleField); - void SendModuleInfo(const ImageEntry& moduleCacheEntry); + void WriteDebugFieldToPacket( uint8_t** ptr, int* currentPacketSize, const ImageDebugInfo& imageDebugInfo ); + void SendModuleInfo( const ImageEntry& moduleCacheEntry ); #endif static tracy_force_inline void SendCallstackSerial( void* ptr ) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index f4bfde2701..2d6b6577bc 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -13,8 +13,6 @@ #ifdef TRACY_HAS_CALLSTACK -constexpr uint32_t ImageCacheBaseCapacity = 512; - #if TRACY_HAS_CALLSTACK == 1 # ifndef NOMINMAX # define NOMINMAX @@ -119,9 +117,9 @@ extern "C" const char* ___tracy_demangle( const char* mangled ) namespace tracy { - inline bool GetEnvBool(const char* environementVariableName, bool defaultValue) + inline bool GetEnvBool( const char* environementVariableName, bool defaultValue ) { - const char* v = GetEnvVar(environementVariableName); + const char* v = GetEnvVar( environementVariableName ); if (v) return v[0] == '1'; else return defaultValue; } @@ -135,27 +133,27 @@ namespace tracy #endif // TRACY_SYMBOL_OFFLINE_RESOLVE void PreventSymbolResolution() { s_shouldResolveSymbolsOffline = true; } - inline bool IsKernelAddress(uint64_t addr) { + inline bool IsKernelAddress( uint64_t addr ) { return (addr >> 63) != 0; } - void DestroyImageEntry(ImageEntry& entry) + void DestroyImageEntry( ImageEntry& entry ) { - tracy_free_fast(entry.path); + tracy_free_fast( entry.path ); entry.path = nullptr; - tracy_free_fast(entry.name); + tracy_free_fast( entry.name ); entry.name = nullptr; - tracy_free(entry.degugModuleField.debugData); - entry.degugModuleField.debugData = nullptr; + tracy_free( entry.imageDebugInfo.debugData ); + entry.imageDebugInfo.debugData = nullptr; } class ImageCache { public: - ImageCache(size_t moduleCacheCapacity = ImageCacheBaseCapacity) : m_modCache(moduleCacheCapacity) {} + ImageCache( size_t moduleCacheCapacity = 512 ) : m_modCache( moduleCacheCapacity ) {} ~ImageCache() { Clear(); } - ImageEntry* CacheModuleWithDebugInfo(const ImageEntry& entry) + ImageEntry* CacheModuleWithDebugInfo( const ImageEntry& entry ) { m_sorted &= m_modCache.empty() ? true : (entry.start < m_modCache.back().start); ImageEntry* newEntry = m_modCache.push_next(); @@ -163,18 +161,18 @@ namespace tracy return newEntry; } - const ImageEntry* FindEntryFromAddr(uint64_t addr) const + const ImageEntry* FindEntryFromAddr( uint64_t addr ) const { - if (m_sorted) + if( m_sorted ) { - auto it = std::lower_bound(m_modCache.begin(), m_modCache.end(), addr, [](const ImageEntry& lhs, const uint64_t& rhs) { return lhs.start > rhs; }); - if (it != m_modCache.end() && (addr < it->end || it->end == 0)) + auto it = std::lower_bound( m_modCache.begin(), m_modCache.end(), addr, []( const ImageEntry& lhs, const uint64_t& rhs ) { return lhs.start > rhs; } ); + if( it != m_modCache.end() && ( addr < it->end || it->end == 0 ) ) return &(*it); } else { - auto it = std::find_if(m_modCache.begin(), m_modCache.end(), [addr](const ImageEntry& module) { return addr >= module.start && (addr < module.end || module.end == 0); }); - if (it != m_modCache.end()) + auto it = std::find_if( m_modCache.begin(), m_modCache.end(), [addr]( const ImageEntry& module ) { return addr >= module.start && ( addr < module.end || module.end == 0 ); } ); + if( it != m_modCache.end() ) return &(*it); } return nullptr; @@ -182,26 +180,26 @@ namespace tracy void Sort() { - if (!m_sorted) + if( !m_sorted ) { - std::sort(m_modCache.begin(), m_modCache.end(), [](const ImageEntry& lhs, const ImageEntry& rhs) { return lhs.start > rhs.start; }); + std::sort( m_modCache.begin(), m_modCache.end(), []( const ImageEntry& lhs, const ImageEntry& rhs ) { return lhs.start > rhs.start; } ); m_sorted = true; } } void Clear() { - for (ImageEntry& cacheEntry : m_modCache) + for( ImageEntry& cacheEntry : m_modCache ) { - DestroyImageEntry(cacheEntry); + DestroyImageEntry( cacheEntry ); } m_modCache.clear(); m_sorted = true; } - bool ContainsModule(uint64_t startAddress) + bool ContainsModule( uint64_t startAddress ) { - const ImageEntry* moduleInfo = FindEntryFromAddr(startAddress); + const ImageEntry* moduleInfo = FindEntryFromAddr( startAddress ); return moduleInfo && moduleInfo->start == startAddress; } @@ -245,7 +243,7 @@ class ImageCacheLibbacktrace : public ImageCache { const ImageEntry* entry = FindEntryFromAddr( address ); - if (!entry) + if( !entry ) { Refresh(); return FindEntryFromAddr( address ); @@ -312,7 +310,7 @@ class ImageCacheLibbacktrace : public ImageCache return; } - for(ImageEntry& entry : m_modCache) + for( ImageEntry& entry : m_modCache ) { if( entry.name == nullptr ) { @@ -357,24 +355,24 @@ static ImageCache* s_krnlSymbolsCache = nullptr; 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(); + 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) + if ( s_krnlCache != nullptr ) { s_krnlCache->~ImageCache(); - tracy_free((void*)s_krnlCache); + tracy_free( (void*)s_krnlCache ); s_krnlCache = nullptr; } - if (s_imageCache != nullptr) + if ( s_imageCache != nullptr ) { s_imageCache->~UserlandImageCache(); - tracy_free(s_imageCache); + tracy_free( s_imageCache ); s_imageCache = nullptr; } @@ -436,69 +434,52 @@ struct CV_INFO_PDB70 BYTE PdbFileName[1]; }; -static constexpr auto mandatoryAlignment = 8; static constexpr DWORD CV_SIGNATURE_RSDS = 'SDSR'; // 'SDSR' -uint64_t DbgHelpLoadSymbolsForModule(const char* imageName, uint64_t baseOfDll, uint32_t dllSize) -{ - auto d = SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0); - - IMAGEHLP_MODULEW64 moduleInfo{}; - moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); - if (TRUE == SymGetModuleInfoW64(s_DbgHelpSymHandle, (uintptr_t)baseOfDll, &moduleInfo)) - { - wprintf(L"- ImageName=%s\n", moduleInfo.ImageName); - wprintf(L"- LoadedPdbName=%s\n", moduleInfo.LoadedPdbName); - } - - return d; -} - - -bool GetModuleInfoFromPEHeaders(uint64_t baseOfDll, DebugFormat* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize) +bool GetModuleInfoFromPEHeaders( uint64_t baseOfDll, ImageDebugFormat* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize ) { static constexpr bool MappedAsImage = true; PVOID BaseAddress = (void*)baseOfDll; - PIMAGE_NT_HEADERS header = ImageNtHeader(BaseAddress); + PIMAGE_NT_HEADERS header = ImageNtHeader( BaseAddress ); ULONG debugDirectoryCount = 0; IMAGE_SECTION_HEADER* debugSectionHeader; - PVOID debugSectionData = ImageDirectoryEntryToDataEx(BaseAddress, true, IMAGE_DIRECTORY_ENTRY_DEBUG, - &debugDirectoryCount, &debugSectionHeader); + PVOID debugSectionData = ImageDirectoryEntryToDataEx( BaseAddress, true, IMAGE_DIRECTORY_ENTRY_DEBUG, + &debugDirectoryCount, &debugSectionHeader ); - if (debugSectionData == NULL) + if( debugSectionData == NULL ) { return false; } - IMAGE_DEBUG_DIRECTORY* debugDirectory = static_cast(debugSectionData); + IMAGE_DEBUG_DIRECTORY* debugDirectory = static_cast( debugSectionData ); - for (size_t i = 0; (i * sizeof(IMAGE_DEBUG_DIRECTORY)) < debugDirectoryCount; i++) + for( size_t i=0; (i * sizeof( IMAGE_DEBUG_DIRECTORY ) ) < debugDirectoryCount; i++ ) { const IMAGE_DEBUG_DIRECTORY& curDebugDirectory = debugDirectory[i]; - if (debugDirectory[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW) continue; + if( debugDirectory[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW ) continue; - CV_INFO_PDB70* pData = (CV_INFO_PDB70*)(uintptr_t(BaseAddress) + - (MappedAsImage + CV_INFO_PDB70* pData = (CV_INFO_PDB70*)(uintptr_t( BaseAddress ) + + ( MappedAsImage ? debugDirectory[i].AddressOfRawData : debugDirectory[i].PointerToRawData) ); - if (pData->CvSignature != CV_SIGNATURE_RSDS) continue; + if( pData->CvSignature != CV_SIGNATURE_RSDS ) continue; - *debugFormat = DebugFormat::PdbDebugFormat; + *debugFormat = ImageDebugFormat::PdbDebugFormat; - const uint32_t pdbFileLength = strlen((const char*)pData->PdbFileName); - const uint32_t debugFormatSize = sizeof(WindowsDebugData) + pdbFileLength + 1; - *debugInformationData = (uint8_t*)tracy_malloc(debugFormatSize); + const uint32_t pdbFileLength = strlen( (const char*)pData->PdbFileName ); + const uint32_t debugFormatSize = sizeof( PEImageDebugData ) + pdbFileLength + 1; + *debugInformationData = (uint8_t*)tracy_malloc( debugFormatSize ); *debugInformationSize = debugFormatSize; - uint8_t* ptrToDebugPacket = reinterpret_cast(*debugInformationData); - WindowsDebugData* ptrToWindowsDebugData = reinterpret_cast(ptrToDebugPacket); + uint8_t* ptrToDebugPacket = reinterpret_cast( *debugInformationData ); + PEImageDebugData* ptrToWindowsDebugData = reinterpret_cast( ptrToDebugPacket ); // write minor major version ptrToWindowsDebugData->majorVersion = curDebugDirectory.MajorVersion; @@ -507,79 +488,77 @@ bool GetModuleInfoFromPEHeaders(uint64_t baseOfDll, DebugFormat* debugFormat, ui ptrToWindowsDebugData->exeDataTimeStamp = curDebugDirectory.TimeDateStamp; ptrToWindowsDebugData->cvInfo.Age = pData->Age; ptrToWindowsDebugData->cvInfo.CvSignature = pData->CvSignature; - static_assert(sizeof(GUID) == sizeof(pData->Signature), "GUID size must match"); - memcpy(&ptrToWindowsDebugData->cvInfo.Signature, &pData->Signature, sizeof(pData->Signature)); - - memcpy(ptrToDebugPacket + sizeof(WindowsDebugData), pData->PdbFileName, pdbFileLength + 1); + static_assert( sizeof( GUID ) == sizeof( pData->Signature ), "GUID size must match" ); + memcpy( &ptrToWindowsDebugData->cvInfo.Signature, &pData->Signature, sizeof( pData->Signature ) ); + memcpy( ptrToDebugPacket + sizeof( PEImageDebugData ), pData->PdbFileName, pdbFileLength + 1 ); return true; } return false; } -void GetModuleInfoFromDbgHelp(const char* imageName, ImageEntry* moduleEntry) +void GetModuleInfoFromDbgHelp( const char* imageName, ImageEntry* moduleEntry ) { IMAGEHLP_MODULE64 moduleInfo{}; - moduleInfo.SizeOfStruct = sizeof(IMAGEHLP_MODULE64); - if (TRUE == SymGetModuleInfo64(s_DbgHelpSymHandle, moduleEntry->start, &moduleInfo)) + moduleInfo.SizeOfStruct = sizeof( IMAGEHLP_MODULE64 ); + if( TRUE == SymGetModuleInfo64( s_DbgHelpSymHandle, moduleEntry->start, &moduleInfo ) ) { - if (moduleInfo.SymType == SymDeferred) // If symbol loading was deferred, force load it so that we can retrieve the debug informations + if( moduleInfo.SymType == SymDeferred ) // If symbol loading was deferred, force load it so that we can retrieve the debug informations { DWORD prevOptions = SymGetOptions(); - SymSetOptions(prevOptions & (~SYMOPT_DEFERRED_LOADS)); - DWORD64 loadedAddr = SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, imageName, nullptr, moduleEntry->start, moduleEntry->end ? (moduleEntry->end - moduleEntry->start) : 0, nullptr, 0); - SymSetOptions(prevOptions); - if(!SymGetModuleInfo64(s_DbgHelpSymHandle, moduleEntry->start, &moduleInfo)) + SymSetOptions( prevOptions & (~SYMOPT_DEFERRED_LOADS) ); + DWORD64 loadedAddr = SymLoadModuleEx( s_DbgHelpSymHandle, nullptr, imageName, nullptr, moduleEntry->start, moduleEntry->end ? (moduleEntry->end - moduleEntry->start) : 0, nullptr, 0 ); + SymSetOptions( prevOptions ); + if( !SymGetModuleInfo64( s_DbgHelpSymHandle, moduleEntry->start, &moduleInfo ) ) { return; } } - if (moduleInfo.CVSig != CV_SIGNATURE_RSDS) // Do we have a pdb ? + if( moduleInfo.CVSig != CV_SIGNATURE_RSDS ) // Do we have a pdb ? return; - DegugModuleField& debugInfo = moduleEntry->degugModuleField; - debugInfo.debugFormat = DebugFormat::PdbDebugFormat; + ImageDebugInfo& debugInfo = moduleEntry->imageDebugInfo; + debugInfo.debugFormat = ImageDebugFormat::PdbDebugFormat; - const uint32_t pdbFileNameLen = static_cast(strlen(moduleInfo.CVData)); - debugInfo.debugDataSize = sizeof(WindowsDebugData) + (pdbFileNameLen + 1); - debugInfo.debugData = (uint8_t*)tracy_malloc(debugInfo.debugDataSize); - WindowsDebugData* ptrToWindowsDebugData = reinterpret_cast(debugInfo.debugData); + const uint32_t pdbFileNameLen = static_cast( strlen( moduleInfo.CVData ) ); + debugInfo.debugDataSize = sizeof( PEImageDebugData ) + ( pdbFileNameLen + 1 ); + debugInfo.debugData = (uint8_t*)tracy_malloc( debugInfo.debugDataSize ); + PEImageDebugData* ptrToWindowsDebugData = reinterpret_cast( debugInfo.debugData ); ptrToWindowsDebugData->majorVersion = 0; ptrToWindowsDebugData->minorVersion = 0; ptrToWindowsDebugData->exeDataTimeStamp = moduleInfo.TimeDateStamp; - TracyPdbInfo* pdbInfo = &ptrToWindowsDebugData->cvInfo; + PdbInfo* pdbInfo = &ptrToWindowsDebugData->cvInfo; pdbInfo->Age = moduleInfo.PdbAge; pdbInfo->CvSignature = moduleInfo.CVSig; - static_assert(sizeof(pdbInfo->Signature) == sizeof(moduleInfo.PdbSig70), "GUID size must match"); - memcpy(&pdbInfo->Signature, &moduleInfo.PdbSig70, sizeof(moduleInfo.PdbSig70)); + static_assert( sizeof( pdbInfo->Signature ) == sizeof( moduleInfo.PdbSig70 ), "GUID size must match" ); + memcpy( &pdbInfo->Signature, &moduleInfo.PdbSig70, sizeof( moduleInfo.PdbSig70 ) ); - const GUID* guidd = reinterpret_cast(&pdbInfo->Signature); + const GUID* guidd = reinterpret_cast( &pdbInfo->Signature ); - char* pdbFileName = (char*)debugInfo.debugData + sizeof(WindowsDebugData); - memcpy(pdbFileName, moduleInfo.CVData, pdbFileNameLen + 1); + char* pdbFileName = (char*)debugInfo.debugData + sizeof( PEImageDebugData ); + memcpy( pdbFileName, moduleInfo.CVData, pdbFileNameLen + 1 ); } } -ImageEntry* CacheModuleInfo(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) +ImageEntry* CacheModuleInfo( const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) { - ImageEntry moduleEntry = {}; moduleEntry.start = baseOfDll; moduleEntry.end = baseOfDll + dllSize; - moduleEntry.path = CopyStringFast(imageName, imageNameLength); - FormatImageName(&moduleEntry.name, imageName, imageNameLength); + moduleEntry.path = CopyStringFast( imageName, imageNameLength ); + FormatImageName( &moduleEntry.name, imageName, imageNameLength ); - DebugFormat debugFormat = DebugFormat::NoDebugFormat; + ImageDebugFormat debugFormat = ImageDebugFormat::NoDebugFormat; uint8_t* debugData = nullptr; uint32_t debugDataSize = 0; - if (GetModuleInfoFromPEHeaders(moduleEntry.start, &debugFormat, &debugData, &debugDataSize)) + if( GetModuleInfoFromPEHeaders( moduleEntry.start, &debugFormat, &debugData, &debugDataSize ) ) { - DegugModuleField& dmf = moduleEntry.degugModuleField; + ImageDebugInfo& dmf = moduleEntry.imageDebugInfo; dmf.debugFormat = debugFormat; dmf.debugData = debugData; @@ -587,14 +566,15 @@ ImageEntry* CacheModuleInfo(const char* imageName, uint32_t imageNameLength, uin } std::lock_guard mutexguard{ s_cacheMutex }; - return s_imageCache->CacheModuleWithDebugInfo(moduleEntry); + return s_imageCache->CacheModuleWithDebugInfo( moduleEntry ); } -ImageEntry* LoadSymbolsForModuleAndCache(const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize) +ImageEntry* LoadSymbolsForModuleAndCache( const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) { - DbgHelpLoadSymbolsForModule(imageName, baseOfDll, dllSize); - - return CacheModuleInfo(imageName, imageNameLength, baseOfDll, dllSize); + assert( s_DbgHelpSymHandle == GetCurrentProcess() ); // Only resolve we path if resolving current process + SymLoadModuleEx( s_DbgHelpSymHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0 ); + + return CacheModuleInfo( imageName, imageNameLength, baseOfDll, dllSize ); } struct ModuleNameAndBaseAddress @@ -603,47 +583,47 @@ struct ModuleNameAndBaseAddress uint64_t baseAddr; }; -ModuleNameAndBaseAddress OnFailedFindAddress(uint64_t address) +ModuleNameAndBaseAddress TryToLoadModuleDebugInfoAndCache( uint64_t address ) { InitRpmalloc(); HMODULE mod[1024]; DWORD needed; HANDLE proc = GetCurrentProcess(); - if (EnumProcessModules(proc, mod, sizeof(mod), &needed) != 0) + if( EnumProcessModules( proc, mod, sizeof( mod ), &needed ) != 0 ) { const auto sz = needed / sizeof(HMODULE); - for (size_t i = 0; i < sz; i++) + for(size_t i=0; i= base && address < base + info.SizeOfImage) + if ( address >= base && address < base + info.SizeOfImage ) { char name[1024]; - const auto nameLength = GetModuleFileNameA(mod[i], name, 1024); - if (nameLength > 0) + const auto nameLength = GetModuleFileNameA( mod[i], name, 1024 ); + if ( nameLength > 0 ) { // since this is the first time we encounter this module, load its symbols (needed for modules loaded after SymInitialize) - ImageEntry* ImageEntry = LoadSymbolsForModuleAndCache(name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage); + ImageEntry* ImageEntry = LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); return ModuleNameAndBaseAddress{ ImageEntry->name, ImageEntry->start }; } } } } } - return {}; + return { nullptr, 0 }; } -ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols(uint64_t addr, bool* failed) +ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols( uint64_t addr, bool* failed ) { - if (IsKernelAddress(addr)) + if ( IsKernelAddress( addr ) ) { - if (s_krnlCache) + if ( s_krnlCache ) { - const ImageEntry* entry = s_krnlCache->FindEntryFromAddr(addr); - if (entry) + const ImageEntry* entry = s_krnlCache->FindEntryFromAddr( addr ); + if ( entry ) { return ModuleNameAndBaseAddress{ entry->name, entry->start }; } @@ -651,9 +631,9 @@ ModuleNameAndBaseAddress GetModuleNameAndPrepareSymbols(uint64_t addr, bool* fai return ModuleNameAndBaseAddress{ "", addr }; } - const ImageEntry* entry = s_imageCache->FindEntryFromAddr(addr); + const ImageEntry* entry = s_imageCache->FindEntryFromAddr( addr ); - if (entry != nullptr) + if ( entry != nullptr ) return ModuleNameAndBaseAddress{ entry->name, entry->start }; @@ -669,21 +649,21 @@ void InitCallstackCritical() ___tracy_RtlWalkFrameChain = (___tracy_t_RtlWalkFrameChain)GetProcAddress(GetModuleHandleA("ntdll.dll"), "RtlWalkFrameChain"); } -void DbgHelpInit(HANDLE symHandle, bool invadeProcess) +void DbgHelpInit( HANDLE symHandle, bool invadeProcess ) { assert(s_DbgHelpSymHandle == 0); s_DbgHelpSymHandle = symHandle; - HMODULE DbgHelpHdl = GetModuleHandleA("dbghelp.dll"); - if (!DbgHelpHdl) { + HMODULE DbgHelpHdl = GetModuleHandleA( "dbghelp.dll" ); + if ( !DbgHelpHdl ) { TracyDebug("Couldn't load DbgHelp.dll\n"); return; } - _SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress(DbgHelpHdl, "SymAddrIncludeInlineTrace"); - _SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress(DbgHelpHdl, "SymQueryInlineTrace"); - _SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress(DbgHelpHdl, "SymFromInlineContext"); - _SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress(DbgHelpHdl, "SymGetLineFromInlineContext"); + _SymAddrIncludeInlineTrace = (t_SymAddrIncludeInlineTrace)GetProcAddress( DbgHelpHdl, "SymAddrIncludeInlineTrace" ); + _SymQueryInlineTrace = (t_SymQueryInlineTrace)GetProcAddress( DbgHelpHdl, "SymQueryInlineTrace" ); + _SymFromInlineContext = (t_SymFromInlineContext)GetProcAddress( DbgHelpHdl, "SymFromInlineContext" ); + _SymGetLineFromInlineContext = (t_SymGetLineFromInlineContext)GetProcAddress( DbgHelpHdl, "SymGetLineFromInlineContext" ); #ifdef TRACY_DBGHELP_LOCK DBGHELP_INIT; @@ -699,9 +679,9 @@ void DbgHelpInit(HANDLE symHandle, bool invadeProcess) #endif ); - if (!SymInitialize(symHandle, nullptr, false)) + if ( !SymInitialize( symHandle, nullptr, false ) ) { - TracyDebug("Failed to initalize DbgHelp %x\n", GetLastError()); + TracyDebug( "Failed to initalize DbgHelp %x\n", GetLastError() ); } #ifdef TRACY_DBGHELP_LOCK @@ -714,7 +694,7 @@ static void CacheProcessDrivers() { DWORD needed; LPVOID dev[4096]; - if (EnumDeviceDrivers(dev, sizeof(dev), &needed) != 0) + if( EnumDeviceDrivers( dev, sizeof(dev), &needed ) != 0 ) { char windir[MAX_PATH]; if( !GetWindowsDirectoryA( windir, sizeof( windir ) ) ) memcpy( windir, "c:\\windows", 11 ); @@ -730,15 +710,15 @@ static void CacheProcessDrivers() { auto buf = (char*)tracy_malloc_fast( len+3 ); buf[0] = '<'; - memcpy(buf + 1, fn, len); - memcpy(buf + len + 1, ">", 2); + memcpy( buf + 1, fn, len ); + memcpy( buf + len + 1, ">", 2 ); ImageEntry kernelDriver{}; kernelDriver.start = (uint64_t)dev[i]; kernelDriver.end = 0; kernelDriver.name = buf; kernelDriver.path = nullptr; - kernelDriver.degugModuleField = {}; + kernelDriver.imageDebugInfo = {}; const auto len = GetDeviceDriverFileNameA( dev[i], fn, sizeof( fn ) ); if( len != 0 ) @@ -753,21 +733,22 @@ static void CacheProcessDrivers() path = full; } - kernelDriver.path = CopyStringFast(path); + kernelDriver.path = CopyStringFast( path ); - if (SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0)) + if( SymLoadModuleEx( s_DbgHelpSymHandle, nullptr, path, nullptr, (DWORD64)dev[i], 0, nullptr, 0 ) ) { // Kernel drivers PE headers are not accessible from userland, use DbgHelp to retrieve debug info. - GetModuleInfoFromDbgHelp(path, kernelDriver); + GetModuleInfoFromDbgHelp( path, &kernelDriver ); // We no longer need it if we resolve symbols offline, unload it. - if (s_shouldResolveSymbolsOffline) { - SymUnloadModule64(s_DbgHelpSymHandle, (DWORD64)dev[i]); + if( s_shouldResolveSymbolsOffline ) + { + SymUnloadModule64( s_DbgHelpSymHandle, (DWORD64)dev[i] ); } } } - s_krnlCache->CacheModuleWithDebugInfo(kernelDriver); - assert(kernelDriver.end == 0 && "kernel end should be zero"); + s_krnlCache->CacheModuleWithDebugInfo( kernelDriver ); + assert( kernelDriver.end == 0 && "kernel end should be zero" ); cnt++; } } @@ -780,21 +761,21 @@ static void CacheProcessModules() DWORD needed; HANDLE proc = GetCurrentProcess(); HMODULE mod[1024]; - if (EnumProcessModules(proc, mod, sizeof(mod), &needed) != 0) + if( EnumProcessModules( proc, mod, sizeof(mod), &needed ) != 0 ) { const auto sz = needed / sizeof(HMODULE); - for (size_t i = 0; i < sz; i++) + for( size_t i=0; i 0) + const auto nameLength = GetModuleFileNameA( mod[i], name, 1021 ); + if( nameLength > 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); + LoadSymbolsForModuleAndCache( name, nameLength, (DWORD64)info.lpBaseOfDll, info.SizeOfImage ); } } } @@ -860,9 +841,9 @@ void EndCallstack() } } -const char* DecodeCallstackPtrFast(uint64_t ptr) +const char* DecodeCallstackPtrFast( uint64_t ptr ) { - if (s_shouldResolveSymbolsOffline) return "[unresolved]"; + if( s_shouldResolveSymbolsOffline ) return "[unresolved]"; static char ret[MaxNameSize]; const auto proc = GetCurrentProcess(); @@ -890,44 +871,43 @@ const char* DecodeCallstackPtrFast(uint64_t ptr) return ret; } -const char* GetKernelModulePath(uint64_t addr) +const char* GetKernelModulePath( uint64_t addr ) { - assert(addr >> 63 != 0); - if (!s_krnlCache) return nullptr; + assert( addr >> 63 != 0 ); + if( !s_krnlCache ) return nullptr; - const ImageEntry* imageEntry = s_krnlCache->FindEntryFromAddr(addr); - if (imageEntry) - return imageEntry->path; + const ImageEntry* imageEntry = s_krnlCache->FindEntryFromAddr( addr ); + if( imageEntry ) return imageEntry->path; return nullptr; } -bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, DebugFormat debugFormat, const uint8_t* debugData, uint32_t debugDataSize) +bool LoadFromPdb( const char* moduleName, uint64_t baseAddress, uint64_t dllSize, ImageDebugFormat debugFormat, const uint8_t* debugData, uint32_t debugDataSize ) { - assert(debugFormat == DebugFormat::PdbDebugFormat); + assert( debugFormat == ImageDebugFormat::PdbDebugFormat ); - const uint32_t DataForDebugSize = static_cast(debugDataSize); - assert(moduleName != nullptr); + const uint32_t DataForDebugSize = static_cast( debugDataSize ); + assert( moduleName != nullptr ); - const tracy::WindowsDebugData* windowsDebugData = (const tracy::WindowsDebugData*)(debugData); - const size_t pdbPathLength = debugDataSize - sizeof(tracy::WindowsDebugData); - const uint8_t* pdbPath = debugData + sizeof(tracy::WindowsDebugData); + const tracy::PEImageDebugData* windowsDebugData = (const tracy::PEImageDebugData*)( debugData ); + const size_t pdbPathLength = debugDataSize - sizeof( tracy::PEImageDebugData ); + const uint8_t* pdbPath = debugData + sizeof( tracy::PEImageDebugData ); - static_assert(sizeof(WindowsDebugData) == 32, "Structure changed or not properly packed"); + static_assert( sizeof( PEImageDebugData ) == 32, "Structure changed or not properly packed" ); - static_assert(offsetof(CV_INFO_PDB70, CvSignature) == offsetof(TracyPdbInfo, CvSignature), "Mismatch with DbgHelp headers."); - static_assert(offsetof(CV_INFO_PDB70, Signature) == offsetof(TracyPdbInfo, Signature), "Mismatch with DbgHelp headers."); - static_assert(offsetof(CV_INFO_PDB70, Age) == offsetof(TracyPdbInfo, Age), "Mismatch with DbgHelp headers."); - static_assert(offsetof(CV_INFO_PDB70, PdbFileName) == sizeof(TracyPdbInfo), "Mismatch with DbgHelp headers."); + static_assert( offsetof( CV_INFO_PDB70, CvSignature ) == offsetof( PdbInfo, CvSignature ), "Mismatch with DbgHelp headers." ); + static_assert( offsetof( CV_INFO_PDB70, Signature ) == offsetof( PdbInfo, Signature ), "Mismatch with DbgHelp headers." ); + static_assert( offsetof( CV_INFO_PDB70, Age ) == offsetof( PdbInfo, Age ), "Mismatch with DbgHelp headers." ); + static_assert( offsetof( CV_INFO_PDB70, PdbFileName ) == sizeof( PdbInfo ), "Mismatch with DbgHelp headers." ); - const uint32_t sizeOfPdbData = DataForDebugSize - offsetof(WindowsDebugData, cvInfo); + const uint32_t sizeOfPdbData = DataForDebugSize - offsetof( PEImageDebugData, cvInfo ); - auto const debug_module_info_size = sizeof(IMAGE_DEBUG_DIRECTORY) + sizeOfPdbData; - auto const debug_module_info_size_aligned = (debug_module_info_size + mandatoryAlignment) & (~uint64_t(mandatoryAlignment - 1)); + static constexpr auto mandatoryAlignment = 8; - std::vector dataAligned; - dataAligned.resize(debug_module_info_size_aligned); + auto const debug_module_info_size = sizeof( IMAGE_DEBUG_DIRECTORY ) + sizeOfPdbData; + auto const debug_module_info_size_aligned = ( debug_module_info_size + mandatoryAlignment ) & ( ~uint64_t( mandatoryAlignment - 1 ) ); - IMAGE_DEBUG_DIRECTORY* info = reinterpret_cast(dataAligned.data()); + uint8_t* dataBuffer = static_cast( tracy_malloc( debug_module_info_size_aligned ) ); + IMAGE_DEBUG_DIRECTORY* info = reinterpret_cast( dataBuffer ); info->TimeDateStamp = windowsDebugData->exeDataTimeStamp; info->Characteristics = 0; @@ -935,29 +915,27 @@ bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, info->MinorVersion = windowsDebugData->minorVersion; info->Type = IMAGE_DEBUG_TYPE_CODEVIEW; info->AddressOfRawData = 0; - info->PointerToRawData = sizeof(IMAGE_DEBUG_DIRECTORY); + info->PointerToRawData = sizeof( IMAGE_DEBUG_DIRECTORY ); info->SizeOfData = sizeOfPdbData; - memcpy(dataAligned.data() + info->PointerToRawData, &windowsDebugData->cvInfo, sizeOfPdbData); + memcpy( dataBuffer + info->PointerToRawData, &windowsDebugData->cvInfo, sizeOfPdbData ); MODLOAD_DATA module_load_info; - module_load_info.ssize = sizeof(module_load_info); + module_load_info.ssize = sizeof( module_load_info ); module_load_info.ssig = DBHHEADER_DEBUGDIRS; - module_load_info.data = dataAligned.data(); - module_load_info.size = static_cast(dataAligned.size()); + module_load_info.data = dataBuffer; + module_load_info.size = static_cast( debug_module_info_size_aligned ); module_load_info.flags = 0; - const CV_INFO_PDB70* dummie = reinterpret_cast(&windowsDebugData->cvInfo); + DWORD64 loaddedModule = SymLoadModuleEx( s_DbgHelpSymHandle, NULL, moduleName, NULL, baseAddress, + dllSize, &module_load_info, 0 ); + tracy_free( dataBuffer ); - DWORD64 loaddedModule = SymLoadModuleEx(s_DbgHelpSymHandle, NULL, moduleName, NULL, baseAddress, - dllSize, &module_load_info, 0); IMAGEHLP_MODULEW64 moduleInfoDebug{}; - moduleInfoDebug.SizeOfStruct = sizeof(IMAGEHLP_MODULEW64); - - if (SymGetModuleInfoW64(s_DbgHelpSymHandle, loaddedModule, &modulInfoDebug) == TRUE) + if( SymGetModuleInfoW64( s_DbgHelpSymHandle, loaddedModule, &moduleInfoDebug ) == TRUE ) { // Consider deferred to be failing too as it may fail later. We might want to handle failure in the UI. if( moduleInfoDebug.SymType != SymNone && moduleInfoDebug.SymType != SymDeferred ) @@ -969,51 +947,51 @@ bool LoadFromPdb(const char* moduleName, uint64_t baseAddress, uint64_t dllSize, } // Called from the profiler (server) only, we received data from the client. -void CacheModuleAndLoadExternal(ImageEntry& imageEntry) +void CacheModuleAndLoadExternal( ImageEntry& imageEntry ) { - if (IsKernelAddress(imageEntry.start)) + if ( IsKernelAddress( imageEntry.start ) ) { - s_krnlCache->CacheModuleWithDebugInfo(imageEntry); + s_krnlCache->CacheModuleWithDebugInfo( imageEntry ); } else { - s_imageCache->CacheModuleWithDebugInfo(imageEntry); + s_imageCache->CacheModuleWithDebugInfo( imageEntry ); } bool hasSymbolInfo = false; - if (imageEntry.degugModuleField.debugFormat == DebugFormat::PdbDebugFormat) + if( imageEntry.imageDebugInfo.debugFormat == ImageDebugFormat::PdbDebugFormat ) { char* nameFixed = nullptr; - if (IsKernelAddress(imageEntry.start)) + if( IsKernelAddress( imageEntry.start ) ) { - const size_t nameLen = strlen(imageEntry.name); - if (nameLen > 3 && imageEntry.name[0] == '<' && imageEntry.name[nameLen - 1] == '>') + const size_t nameLen = strlen( imageEntry.name ); + if( nameLen > 3 && imageEntry.name[0] == '<' && imageEntry.name[nameLen - 1] == '>' ) { - char* kernelName = CopyStringFast(imageEntry.name + 1); + char* kernelName = CopyStringFast( imageEntry.name + 1 ); kernelName[nameLen - 2] = 0; // replace > nameFixed = kernelName; } } - hasSymbolInfo = LoadFromPdb(nameFixed ? nameFixed : imageEntry.name, imageEntry.start, imageEntry.end - imageEntry.start, - imageEntry.degugModuleField.debugFormat, imageEntry.degugModuleField.debugData, imageEntry.degugModuleField.debugDataSize); + hasSymbolInfo = LoadFromPdb( nameFixed ? nameFixed : imageEntry.name, imageEntry.start, imageEntry.end ? (imageEntry.end - imageEntry.start) : 0, + imageEntry.imageDebugInfo.debugFormat, imageEntry.imageDebugInfo.debugData, imageEntry.imageDebugInfo.debugDataSize ); - if (nameFixed) tracy_free_fast(nameFixed); + if( nameFixed ) tracy_free_fast( nameFixed ); } - if (!hasSymbolInfo) + if ( !hasSymbolInfo ) { // TODO: load from path only if we can check we got the correct binary (check timestamp / guid ?) // That would however require to disable deferred symbol loading or use FindExecutableImageEx - //DbgHelpLoadSymbolsForModule(ImageEntry.path, ImageEntry.start, ImageEntry.end - ImageEntry.start); + // SymLoadModuleEx(s_DbgHelpSymHandle, nullptr, imageEntry.name, nullptr, baseOfDll, dllSize, nullptr, 0); } } -CallstackSymbolData DecodeSymbolAddress(uint64_t ptr) +CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) { CallstackSymbolData sym; - if (s_shouldResolveSymbolsOffline) + if( s_shouldResolveSymbolsOffline ) { sym.file = "[unknown]"; sym.line = 0; @@ -1023,12 +1001,12 @@ CallstackSymbolData DecodeSymbolAddress(uint64_t ptr) IMAGEHLP_LINE64 line; DWORD displacement = 0; - line.SizeOfStruct = sizeof(IMAGEHLP_LINE64); + line.SizeOfStruct = sizeof( IMAGEHLP_LINE64 ); #ifdef TRACY_DBGHELP_LOCK DBGHELP_LOCK; #endif - const auto res = SymGetLineFromAddr64(s_DbgHelpSymHandle, ptr, &displacement, &line); - if (res == 0 || line.LineNumber >= 0xF00000) + const auto res = SymGetLineFromAddr64( s_DbgHelpSymHandle, ptr, &displacement, &line ); + if ( res == 0 || line.LineNumber >= 0xF00000 ) { sym.file = "[unknown]"; sym.line = 0; @@ -1046,7 +1024,7 @@ CallstackSymbolData DecodeSymbolAddress(uint64_t ptr) return sym; } -CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus) +CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus ) { #ifdef TRACY_DBGHELP_LOCK DBGHELP_LOCK; @@ -1055,14 +1033,14 @@ CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _d InitRpmalloc(); bool moduleNotFound = false; - ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols(ptr, &moduleNotFound); + ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols( ptr, &moduleNotFound ); - if (moduleNotFound) + if( moduleNotFound ) { - if (s_DbgHelpSymHandle == GetCurrentProcess()) + if( s_DbgHelpSymHandle == GetCurrentProcess() ) { // We're on the client or self profiling, try to load a potentially new module. - moduleNameAndAddress = OnFailedFindAddress(ptr); + moduleNameAndAddress = TryToLoadModuleDebugInfoAndCache( ptr ); if (moduleNameAndAddress.baseAddr == 0) { // Failed to find the module information. @@ -1078,7 +1056,7 @@ CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _d } } - if (s_shouldResolveSymbolsOffline || moduleNotFound) + if( s_shouldResolveSymbolsOffline || moduleNotFound ) { #ifdef TRACY_DBGHELP_LOCK DBGHELP_UNLOCK; @@ -1120,22 +1098,15 @@ CallstackEntryData DecodeCallstackPtr(uint64_t ptr, DecodeCallStackPtrStatus* _d cb_num = 1; } - char buf[sizeof(SYMBOL_INFO) + MaxNameSize]; + char buf[sizeof( SYMBOL_INFO ) + MaxNameSize]; auto si = (SYMBOL_INFO*)buf; - si->SizeOfStruct = sizeof(SYMBOL_INFO); + si->SizeOfStruct = sizeof( SYMBOL_INFO ); si->MaxNameLen = MaxNameSize; - const auto symValid = SymFromAddr(proc, ptr, nullptr, si) != 0; - - if (symValid) - { - *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; - } - else - { - *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::SymbolMissing; - } + const auto symValid = SymFromAddr( proc, ptr, nullptr, si ) != 0; + if( symValid ) *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; + else *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::SymbolMissing; IMAGEHLP_LINE64 line; @@ -1224,7 +1195,7 @@ int cb_num; CallstackEntry cb_data[MaxCbTrace]; int cb_fixup; -void CacheModuleAndLoadExternal(ImageEntry& ImageEntry) {} +void CacheModuleAndLoadExternal( ImageEntry& ImageEntry ) {} #ifdef TRACY_DEBUGINFOD debuginfod_client* s_debuginfod; @@ -1244,9 +1215,9 @@ static FastVector* s_di_known; static void InitKernelSymbols() { - if (s_krnlSymbolsCache == nullptr) + if ( s_krnlSymbolsCache == nullptr ) { - s_krnlSymbolsCache = new(tracy_malloc(sizeof(ImageCache))) ImageCache(ImageCacheBaseCapacity); + s_krnlSymbolsCache = new( tracy_malloc( sizeof( ImageCache ) ) ) ImageCache(); } FILE* f = fopen( "/proc/kallsyms", "rb" ); @@ -1329,7 +1300,7 @@ static void InitKernelSymbols() kernelSymbol.name = strname; kernelSymbol.path = strmod; - s_krnlSymbolsCache->CacheModuleWithDebugInfo(kernelSymbol); + s_krnlSymbolsCache->CacheModuleWithDebugInfo( kernelSymbol ); } tracy_free_fast( linebuf ); @@ -1409,7 +1380,7 @@ void InitCallstack() #endif //#ifdef TRACY_USE_IMAGE_CACHE #ifndef TRACY_SYMBOL_OFFLINE_RESOLVE - s_shouldResolveSymbolsOffline = GetEnvBool("TRACY_SYMBOL_OFFLINE_RESOLVE", s_shouldResolveSymbolsOffline); + s_shouldResolveSymbolsOffline = GetEnvBool( "TRACY_SYMBOL_OFFLINE_RESOLVE", s_shouldResolveSymbolsOffline ); #endif //#ifndef TRACY_SYMBOL_OFFLINE_RESOLVE if( s_shouldResolveSymbolsOffline ) { @@ -1689,7 +1660,7 @@ void GetSymbolForOfflineResolve(void* address, uint64_t imageBaseAddress, Callst cbEntry.line = 0; } -CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus) +CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus ) { InitRpmalloc(); @@ -1734,13 +1705,13 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ return { cb_data, uint8_t( cb_num ), imageName ? imageName : "[unknown]" }; } #ifdef __linux - else if(s_krnlSymbolsCache) + else if( s_krnlSymbolsCache ) { // This is a symbol cache, kernel image cache is currently unused const ImageEntry* symbolEntry = s_krnlSymbolsCache->FindEntryFromAddr((uint64_t)ptr); - if (symbolEntry) + if ( symbolEntry ) { - cb_data[0].name = CopyStringFast(symbolEntry->name ); + cb_data[0].name = CopyStringFast( symbolEntry->name ); cb_data[0].file = CopyStringFast( "" ); cb_data[0].line = 0; cb_data[0].symLen = symbolEntry->end - symbolEntry->start; @@ -1762,7 +1733,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ #elif TRACY_HAS_CALLSTACK == 5 -void CacheModuleAndLoadExternal(ImageEntry& ImageEntry) {} +void CacheModuleAndLoadExternal( ImageEntry& ImageEntry ) {} void InitCallstackCritical() { @@ -1808,7 +1779,7 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) return CallstackSymbolData { symloc, 0, false, 0 }; } -CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus) +CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus ) { static CallstackEntry cb; cb.line = 0; diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index 0289df7ddc..209d5a2edc 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -53,7 +53,12 @@ enum struct DecodeCallStackPtrStatus Count }; -enum struct DebugFormat : uint8_t; +enum struct ImageDebugFormat : uint8_t +{ + NoDebugFormat, + PdbDebugFormat, + DwarfDebugFormat, +}; struct CallstackSymbolData { @@ -79,9 +84,9 @@ struct CallstackEntryData const char* imageName; }; -struct DegugModuleField +struct ImageDebugInfo { - DebugFormat debugFormat; + ImageDebugFormat debugFormat; uint8_t* debugData; uint32_t debugDataSize; }; @@ -93,7 +98,7 @@ struct ImageEntry char* name; char* path; - DegugModuleField degugModuleField; + ImageDebugInfo imageDebugInfo; }; @@ -102,15 +107,13 @@ std::recursive_mutex& GetModuleCacheMutexForRead(); CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ); const char* DecodeCallstackPtrFast( uint64_t ptr ); -CallstackEntryData DecodeCallstackPtr( uint64_t ptr , DecodeCallStackPtrStatus* _decodeCallStackPtrStatus); +CallstackEntryData DecodeCallstackPtr( uint64_t ptr , DecodeCallStackPtrStatus* _decodeCallStackPtrStatus ); void InitCallstack(); void InitCallstackCritical(); void EndCallstack(); const char* GetKernelModulePath( uint64_t addr ); -void FindModuleFromAddr(uint64_t addr, const ImageEntry** outModule); -void FindKernelDriverFromAddr(uint64_t addr, const ImageEntry** outDrive); void CacheModuleAndLoadExternal(ImageEntry& moduleCacheEntry); @@ -118,9 +121,6 @@ const FastVector* GetUserImageInfos(); const FastVector* GetKernelImageInfos(); -// -//void AddModuleToCache() - #ifdef TRACY_DEBUGINFOD const uint8_t* GetBuildIdForImage( const char* image, size_t& size ); debuginfod_client* GetDebuginfodClient(); diff --git a/public/common/TracyDebugModulesHeaderFile.hpp b/public/common/TracyDebugModulesHeaderFile.hpp index a432ba5fac..91730c05fa 100644 --- a/public/common/TracyDebugModulesHeaderFile.hpp +++ b/public/common/TracyDebugModulesHeaderFile.hpp @@ -1,37 +1,27 @@ #pragma once -#include + +namespace tracy +{ #pragma pack( push, 1 ) -struct TracyPdbInfo + +struct PdbInfo { uint32_t CvSignature; uint8_t Signature[16]; // GUID uint32_t Age; }; -#pragma pack( pop ) -namespace tracy -{ - - - -#pragma pack( push, 1 ) -struct WindowsDebugData +struct PEImageDebugData { uint16_t majorVersion; uint16_t minorVersion; uint32_t exeDataTimeStamp; - TracyPdbInfo cvInfo; + PdbInfo cvInfo; }; -#pragma pack( pop ) -enum struct DebugFormat : uint8_t -{ - NoDebugFormat, - PdbDebugFormat, - DwarfDebugFormat, -}; +#pragma pack( pop ) } diff --git a/public/common/TracyFastVector.hpp b/public/common/TracyFastVector.hpp index 9520e73e3d..38accc926b 100644 --- a/public/common/TracyFastVector.hpp +++ b/public/common/TracyFastVector.hpp @@ -3,7 +3,6 @@ #include #include -#include #include "../common/TracyAlloc.hpp" #include "../common/TracyForceInline.hpp" @@ -57,9 +56,6 @@ class FastVector T& operator[]( size_t idx ) { return m_ptr[idx]; } const T& operator[]( size_t idx ) const { return m_ptr[idx]; } - T& at( size_t idx ) { return m_ptr[idx]; } - const T& at( size_t idx ) const { return m_ptr[idx]; } - T* push_next() { if( m_write == m_end ) AllocMore(); diff --git a/public/tracy/Tracy.hpp b/public/tracy/Tracy.hpp index b7b9c27c65..d8bf9e3fed 100644 --- a/public/tracy/Tracy.hpp +++ b/public/tracy/Tracy.hpp @@ -215,7 +215,7 @@ #define ZoneTransientS( varname, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), nullptr, 0, depth, active ) #define ZoneTransientNS( varname, name, depth, active ) tracy::ScopedZone varname( TracyLine, TracyFile, strlen( TracyFile ), TracyFunction, strlen( TracyFunction ), name, strlen( name ), depth, active ) -#define ZoneScopedS( depth ) ZoneNamedS( ___tracy_scoped_zone, depth, true ) +#define ZoneScopedS( depth ) ZoneNamedS( ___tracy_scoped_zone, depth, true ) #define ZoneScopedNS( name, depth ) ZoneNamedNS( ___tracy_scoped_zone, name, depth, true ) #define ZoneScopedCS( color, depth ) ZoneNamedCS( ___tracy_scoped_zone, color, depth, true ) #define ZoneScopedNCS( name, color, depth ) ZoneNamedNCS( ___tracy_scoped_zone, name, color, depth, true ) diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 0e223131b4..20b43bfe67 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -45,10 +45,6 @@ #include "TracyWorker.hpp" #include "tracy_pdqsort.h" - -#define LOCAL_RESOLVE 1 - - namespace tracy { @@ -265,7 +261,7 @@ static bool IsQueryPrio( ServerQuery type ) LoadProgress Worker::s_loadProgress; -Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const SymbolResolutionConfig& symbolResConfig) +Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const SymbolResolutionConfig& symbolResConfig ) : m_addr( addr ) , m_port( port ) , m_hasData( false ) @@ -273,7 +269,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb , m_buffer( new char[TargetFrameSize*3 + 1] ) , m_bufferOffset( 0 ) , m_inconsistentSamples( false ) - , m_symbolConfig(symbolResConfig) + , m_symbolConfig( symbolResConfig ) , m_pendingStrings( 0 ) , m_pendingThreads( 0 ) , m_pendingFibers( 0 ) @@ -310,7 +306,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb m_threadNet = std::thread( [this] { SetThreadName( "Tracy Network" ); Network(); } ); } -Worker::Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames, const SymbolResolutionConfig& symbolResConfig) +Worker::Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames, const SymbolResolutionConfig& symbolResConfig ) : m_hasData( true ) , m_delay( 0 ) , m_resolution( 0 ) @@ -324,7 +320,7 @@ Worker::Worker( const char* name, const char* program, const std::vectordebugDataSize = -1; + debugField->debugDataSize = 0; debugField->debugData = nullptr; return; } - f.Read(debugField->debugDataSize); - debugField->debugData = (uint8_t*)tracy_malloc(debugField->debugDataSize); - f.Read(debugField->debugData, debugField->debugDataSize); + f.Read( debugField->debugDataSize ); + debugField->debugData = (uint8_t*)tracy_malloc( debugField->debugDataSize ); + f.Read( debugField->debugData, debugField->debugDataSize ); }; - auto DeserializeMallocedFastString = [&](char** s) + auto DeserializeMallocedFastString = [&]( char** s ) { - int sLenght = -1; - f.Read(sLenght); - if (sLenght == -1) + int sLength = -1; + f.Read( sLength ); + if (sLength == -1) + { + *s = nullptr; return; + } - *s = (char*)tracy_malloc_fast(sLenght); - - f.Read(*s, sLenght); + *s = (char*)tracy_malloc_fast( sLength ); + f.Read( *s, sLength ); }; - if ( CurrentVersion >= FileVersion( 0, 11, 3 ) ) - { - uint32_t moduleCount = -1; - f.Read(moduleCount); - for (size_t i = 0; i < moduleCount; i++) - { - ImageEntry imageEntry; - f.Read(imageEntry.start); - f.Read(imageEntry.end); - DeserializeMallocedFastString(&imageEntry.name); - DeserializeMallocedFastString(&imageEntry.path); - - DeserializeDebugField(&imageEntry.degugModuleField); - - if (m_symbolConfig.m_attemptResolutionByWorker) - CacheModuleAndLoadExternal(imageEntry); - } - - f.Read(moduleCount); - for (size_t i = 0; i < moduleCount; i++) + auto DeseriliazeImageEntries = [&]() { - ImageEntry kernelImageEntry; - f.Read(kernelImageEntry.start); - kernelImageEntry.end = 0; - char* name = nullptr; - DeserializeMallocedFastString(&name); - kernelImageEntry.name = name; + uint32_t moduleCount = 0; + f.Read( moduleCount ); - char* path = nullptr; - DeserializeMallocedFastString(&path); - kernelImageEntry.path = path; + for( size_t i=0; i= FileVersion( 0, 11, 3 ) ) + { + DeseriliazeImageEntries(); // UserLandModules + DeseriliazeImageEntries(); // KernelModules } // Post read processing @@ -2360,16 +2344,6 @@ uint64_t Worker::GetSymbolForAddress( uint64_t address, uint32_t& offset ) return it->addr; } -SymbolLocation Worker::GetSymbolLocFromSysAdrees( uint64_t address ) -{ - DoPostponedSymbols(); - auto it = std::lower_bound( m_data.symbolLoc.begin(), m_data.symbolLoc.end(), address, - []( const auto& l, const auto& r ) { return l.addr + l.len < r; } ); - if( it == m_data.symbolLoc.end() || address < it->addr ) return {}; - - return *it; -} - uint64_t Worker::GetInlineSymbolForAddress( uint64_t address ) const { auto it = m_data.codeSymbolMap.find( address ); @@ -2806,10 +2780,7 @@ void Worker::Exec() { case HandshakeWelcome: -#ifdef LOCAL_RESOLVE InitCallstack(); -#endif // LOCAL_RESOLVE - break; case HandshakeProtocolMismatch: case HandshakeNotAvailable: @@ -2870,13 +2841,13 @@ void Worker::Exec() m_hostInfo = welcome.hostInfo; uint32_t serverFlags = 0; - if (m_symbolConfig.m_preventResolutionByClient) + if( m_symbolConfig.m_preventResolutionByClient ) { // We could probably enable this automatically on windows when connected to localhost // or based on some platform filter? For now, simply honor the config. serverFlags |= ServerFlags::PreventSymbolResolution; } - m_sock.Send(&serverFlags, sizeof(serverFlags)); + m_sock.Send( &serverFlags, sizeof(serverFlags) ); if( m_onDemand ) @@ -3015,9 +2986,7 @@ void Worker::Exec() close: -#ifdef LOCAL_RESOLVE EndCallstack(); -#endif // LOCAL_RESOLVE Shutdown(); m_netWriteCv.notify_one(); @@ -3408,10 +3377,10 @@ bool Worker::DispatchProcess( const QueueItem& ev, const char*& ptr ) return true; case QueueType::DataPacket: { - ptr += sizeof(QueueHeader); - const QueueDataPacket* packet = reinterpret_cast(ptr); - ptr += sizeof(QueueDataPacket); - AddDataPacket(reinterpret_cast(ptr), packet->packetSize, packet->packetDataType); + ptr += sizeof( QueueHeader ); + const QueueDataPacket* packet = reinterpret_cast( ptr ); + ptr += sizeof( QueueDataPacket ); + AddDataPacket( reinterpret_cast( ptr ), packet->packetSize, packet->packetDataType ); ptr += packet->packetSize; return true; } @@ -3917,28 +3886,13 @@ void Worker::AddSecondString( const char* str, size_t sz ) m_pendingSecondString = StoreString( str, sz ); } -void Worker::AddDataPacketFailure( const void* data, size_t size, PacketDataType dataType ) -{ - m_pendingDataPacket.type = dataType; - m_pendingDataPacket.data.resize( size ); - memcpy( m_pendingDataPacket.data.data(), data, size ); -} - void Worker::AddDataPacket( const void* data, size_t size, PacketDataType dataType ) { - assert( m_pendingDataPacket.type == PacketDataType::EMPTY && m_pendingDataPacket.data.size() == 0 && - dataType != PacketDataType::EMPTY ); + assert( m_pendingDataPacket.type == PacketDataType::EMPTY && m_pendingDataPacket.data == nullptr && dataType != PacketDataType::EMPTY ); m_pendingDataPacket.type = dataType; - m_pendingDataPacket.data.resize( size ); - memcpy( m_pendingDataPacket.data.data(), data, size ); - -#if 0 - int d[100]; - for( size_t i = 0; i < size / sizeof( int ); i++ ) - d[i] = *reinterpret_cast( &m_pendingDataPacket.data[i * 4] ); - -#endif + m_pendingDataPacket.data = static_cast( tracy_malloc( size ) ); + memcpy( m_pendingDataPacket.data, data, size ); } void Worker::AddExternalName( uint64_t ptr, const char* str, size_t sz ) @@ -4021,8 +3975,7 @@ void Worker::AddSymbolCode( uint64_t ptr, const char* data, size_t sz ) { const auto& op = insn[i]; const auto addr = op.address; - TryResolveCallStackIfNeeded(PackPointer(addr)); - + TryResolveCallStackIfNeeded( PackPointer( addr ) ); uint64_t callAddr = 0; const auto& detail = *op.detail; @@ -4058,9 +4011,9 @@ void Worker::AddSymbolCode( uint64_t ptr, const char* data, size_t sz ) if( callAddr != 0 ) break; } } - if( callAddr != 0) + if( callAddr != 0 ) { - TryResolveCallStackIfNeeded(PackPointer(addr)); + TryResolveCallStackIfNeeded( PackPointer( addr ) ); } } cs_free( insn, cnt ); @@ -4097,24 +4050,24 @@ uint64_t Worker::GetCanonicalPointer( const CallstackFrameId& id ) const return ( id.idx & 0x3FFFFFFFFFFFFFFF ) | ( ( id.idx & 0x3000000000000000 ) << 2 ); } -void Worker::TryResolveCallStackIfNeeded(CallstackFrameId frameId) +void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId ) { // If we already have the information - if (m_data.callstackFrameMap.count(frameId) != 0) + if (m_data.callstackFrameMap.count( frameId ) != 0) { return; } - uint64_t symbolAddress = GetCanonicalPointer(frameId); + uint64_t symbolAddress = GetCanonicalPointer( frameId ); DecodeCallStackPtrStatus status = DecodeCallStackPtrStatus::Count; - if (m_symbolConfig.m_attemptResolutionByWorker) + if( m_symbolConfig.m_attemptResolutionByWorker ) { // TODO: offload to a worker thread - CallstackEntryData outCallStack = DecodeCallstackPtr(symbolAddress, &status); - if (status == DecodeCallStackPtrStatus::Success) + CallstackEntryData outCallStack = DecodeCallstackPtr( symbolAddress, &status ); + if ( status == DecodeCallStackPtrStatus::Success ) { - const uint32_t imageNameIdx = StoreString(outCallStack.imageName).idx; + const uint32_t imageNameIdx = StoreString( outCallStack.imageName , strlen( outCallStack.imageName ) ).idx; assert(outCallStack.size <= 255); const QueueCallstackFrameSize queueCallstackFrameSize = { @@ -4123,30 +4076,44 @@ void Worker::TryResolveCallStackIfNeeded(CallstackFrameId frameId) }; m_pendingCallstackFrames++; - ProcessCallstackFrameSize(queueCallstackFrameSize, imageNameIdx); + ProcessCallstackFrameSize( queueCallstackFrameSize, imageNameIdx ); // we know that for the best we have inline information for (size_t i = 0; i < queueCallstackFrameSize.size; i++) { const auto& frame = outCallStack.data[i]; - ProcessCallstackFrame(frame.line, frame.symAddr, frame.symLen, - StoreString(frame.name).idx, StoreString(frame.file).idx, true); + ProcessCallstackFrame( frame.line, frame.symAddr, frame.symLen, + StoreString( frame.name, strlen( frame.name ) ).idx, StoreString( frame.file, strlen( frame.file ) ).idx, true ); // We copied it, now free the content - tracy_free_fast((void*)frame.name); - tracy_free_fast((void*)frame.file); + tracy_free_fast( (void*)frame.name ); + tracy_free_fast( (void*)frame.file ); } - assert(m_pendingCallstackSubframes == 0); + assert( m_pendingCallstackSubframes == 0 ); } } - // Fallback to asking the client. - // TODO: Only do this if client does not have TRACY_SYMBOL_OFFLINE_RESOLVE - if (status != DecodeCallStackPtrStatus::Success) + + switch (status) { + break; + case tracy::DecodeCallStackPtrStatus::ModuleMissing: + // TODO: actually only query module info, and then try to resolve again locally. + // Or we could just rely on the user triggering a new symbol resolution manually. m_pendingCallstackFrames++; - Query(ServerQueryCallstackFrame, symbolAddress); - return; + Query( ServerQueryCallstackFrame, symbolAddress ); + break; + case tracy::DecodeCallStackPtrStatus::Count: + assert( false && "Missing error handling ?" ); + [[fallthrough]]; + case tracy::DecodeCallStackPtrStatus::SymbolMissing: + // Fallback to asking the client for the symbol since we couldn't find it. + m_pendingCallstackFrames++; + Query( ServerQueryCallstackFrame, symbolAddress ); + break; + case tracy::DecodeCallStackPtrStatus::Success: + default: + break; } } @@ -4178,9 +4145,9 @@ void Worker::AddCallstackPayload( const char* _data, size_t _sz ) m_data.callstackPayload.push_back( arr ); - for (CallstackFrameId frameId : *arr) + for ( CallstackFrameId frameId : *arr ) { - TryResolveCallStackIfNeeded(frameId); + TryResolveCallStackIfNeeded( frameId ); } } else @@ -4268,7 +4235,7 @@ void Worker::AddCallstackAllocPayload( const char* data ) for( auto& frame : *arr ) { - TryResolveCallStackIfNeeded(frame); + TryResolveCallStackIfNeeded( frame ); } } else @@ -4609,16 +4576,18 @@ uint32_t Worker::GetSecondStringIdx() void Worker::AccessPendingData( uint8_t** ptr, size_t* sz, PacketDataType* type ) { - assert( m_pendingDataPacket.type != PacketDataType::EMPTY && m_pendingDataPacket.data.size() != 0 ); + assert( m_pendingDataPacket.type != PacketDataType::EMPTY && m_pendingDataPacket.data != nullptr ); - *ptr = m_pendingDataPacket.data.data(); - *sz = m_pendingDataPacket.data.size(); + *ptr = m_pendingDataPacket.data; + *sz = m_pendingDataPacket.dataSize; *type = m_pendingDataPacket.type; } void Worker::FreePendingData() { - m_pendingDataPacket.data.clear(); + tracy_free( m_pendingDataPacket.data ); + m_pendingDataPacket.data = nullptr; + m_pendingDataPacket.dataSize = 0; m_pendingDataPacket.type = PacketDataType::EMPTY; } @@ -4645,18 +4614,10 @@ StringLocation Worker::StoreString( const char* str, size_t sz ) return ret; } -StringLocation Worker::StoreString(const char* str) -{ - return StoreString(str, strlen(str)); -} - bool Worker::Process( const QueueItem& ev ) { switch( ev.hdr.type ) { - case QueueType::ModuleUpdate: - DispatchImageEntry( ev.imageEntry ); - break; case QueueType::ThreadContext: ProcessThreadContext( ev.threadCtx ); break; @@ -4950,6 +4911,9 @@ bool Worker::Process( const QueueItem& ev ) case QueueType::FiberLeave: ProcessFiberLeave( ev.fiberLeave ); break; + case QueueType::ModuleUpdate: + DispatchImageEntry(ev.imageEntry); + break; default: assert( false ); break; @@ -6710,7 +6674,7 @@ void Worker::ProcessCallstackSampleContextSwitch( const QueueCallstackSample& ev td.ctxSwitchSamples.push_back( sd ); } -void Worker::ProcessCallstackFrameSize(const QueueCallstackFrameSize& ev, uint32_t imageNameIdx) +void Worker::ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev, uint32_t imageNameIdx ) { assert( !m_callstackFrameStaging ); assert( m_pendingCallstackSubframes == 0 ); @@ -6722,13 +6686,13 @@ void Worker::ProcessCallstackFrameSize(const QueueCallstackFrameSize& ev, uint32 #endif // Frames may be duplicated due to recursion - auto fmit = m_data.callstackFrameMap.find(PackPointer(ev.ptr)); - if (fmit == m_data.callstackFrameMap.end()) + auto fmit = m_data.callstackFrameMap.find( PackPointer( ev.ptr ) ); + if( fmit == m_data.callstackFrameMap.end() ) { m_callstackFrameStaging = m_slab.Alloc(); m_callstackFrameStaging->size = ev.size; - m_callstackFrameStaging->data = m_slab.Alloc(ev.size); - m_callstackFrameStaging->imageName = StringIdx(imageNameIdx); + m_callstackFrameStaging->data = m_slab.Alloc( ev.size ); + m_callstackFrameStaging->imageName = StringIdx( imageNameIdx ); m_callstackFrameStagingPtr = ev.ptr; } @@ -6736,18 +6700,18 @@ void Worker::ProcessCallstackFrameSize(const QueueCallstackFrameSize& ev, uint32 void Worker::ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev ) { - ProcessCallstackFrameSize(ev, GetSingleStringIdx()); + ProcessCallstackFrameSize( ev, GetSingleStringIdx() ); } -void Worker::ProcessCallstackFrame(const QueueCallstackFrame& ev, bool querySymbols) +void Worker::ProcessCallstackFrame( const QueueCallstackFrame& ev, bool querySymbols ) { const uint32_t nitidx = GetSingleStringIdx(); const uint32_t fitidx = GetSecondStringIdx(); - ProcessCallstackFrame(ev.line, ev.symAddr, ev.symLen, nitidx, fitidx, querySymbols); + ProcessCallstackFrame( ev.line, ev.symAddr, ev.symLen, nitidx, fitidx, querySymbols ); } -void Worker::ProcessCallstackFrame(uint32_t line, uint64_t symAddr, uint32_t symLen, uint32_t nitidx, uint32_t fitidx, bool querySymbols) +void Worker::ProcessCallstackFrame( uint32_t line, uint64_t symAddr, uint32_t symLen, uint32_t nitidx, uint32_t fitidx, bool querySymbols ) { assert( m_pendingCallstackSubframes > 0 ); @@ -8611,64 +8575,48 @@ void Worker::Write( FileWrite& f, bool fiDict ) - auto SerializeDebugModuleField = [&](const DegugModuleField& debugField) + auto SerializeDebugModuleField = [&]( const ImageDebugInfo& debugField ) { - f.Write(&debugField.debugFormat, sizeof(debugField.debugFormat)); - if (debugField.debugFormat == DebugFormat::NoDebugFormat) + f.Write( &debugField.debugFormat, sizeof( debugField.debugFormat ) ); + if( debugField.debugFormat == ImageDebugFormat::NoDebugFormat ) return; - - f.Write(&debugField.debugDataSize, sizeof(debugField.debugDataSize)); - f.Write(debugField.debugData, debugField.debugDataSize); - + f.Write( &debugField.debugDataSize, sizeof( debugField.debugDataSize ) ); + f.Write( debugField.debugData, debugField.debugDataSize ); }; - auto SerializeString = [&](const char* s) + auto SerializeString = [&]( const char* s ) { - int sLenght = s != nullptr ? strlen(s) + 1 : -1; - f.Write(&sLenght, sizeof(sLenght)); + int sLength = s != nullptr ? strlen( s ) + 1 : -1; + f.Write( &sLength, sizeof( sLength ) ); - if (sLenght == -1) + if ( sLength == -1 ) return; - f.Write(s, sLenght); + f.Write( s, sLength ); }; - const FastVector* userlandImages = GetUserImageInfos(); - uint32_t moduleCount = static_cast(userlandImages ? userlandImages->size() : 0); - f.Write(&moduleCount, sizeof(moduleCount)); - - if (userlandImages) - { - for (const ImageEntry& moduleCacheEntry : *userlandImages) + auto SerializeImageEntries = [&]( const FastVector* imagesEntries ) { - f.Write(&moduleCacheEntry.start, sizeof(moduleCacheEntry.start)); - f.Write(&moduleCacheEntry.end, sizeof(moduleCacheEntry.end)); - - SerializeString(moduleCacheEntry.name); - SerializeString(moduleCacheEntry.path); - - SerializeDebugModuleField(moduleCacheEntry.degugModuleField); - } - } + uint32_t moduleCount = static_cast( imagesEntries ? imagesEntries->size() : 0 ); + f.Write( &moduleCount, sizeof( moduleCount ) ); - const FastVector* kernelImages = GetKernelImageInfos(); - moduleCount = static_cast(kernelImages ? kernelImages->size() : 0); - f.Write(&moduleCount, sizeof(moduleCount)); - - if (kernelImages) - { - for (const ImageEntry& it : *kernelImages) - { - f.Write(&it.start, sizeof(it.start)); - assert(it.end == 0 && "kernel end should be zero"); + if( imagesEntries ) + { + for (const auto& m : *imagesEntries) + { + f.Write( &m.start, sizeof( m.start ) ); + f.Write( &m.end, sizeof( m.end ) ); + SerializeString( m.name ); + SerializeString( m.path ); - SerializeString(it.name); - SerializeString(it.path); + SerializeDebugModuleField( m.imageDebugInfo ); + } + } + }; - SerializeDebugModuleField(it.degugModuleField); - } - } + SerializeImageEntries( GetUserImageInfos() ); + SerializeImageEntries( GetKernelImageInfos() ); } @@ -8933,29 +8881,29 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) ptrToData += sizeof( baseAddress ); uint64_t end = MemRead(ptrToData); - ptrToData += sizeof(end); + ptrToData += sizeof( end ); uint32_t moduleNameSize = MemRead( ptrToData ); - ptrToData += sizeof(moduleNameSize); + ptrToData += sizeof( moduleNameSize ); - assert((char)(ptrToData[moduleNameSize - 1]) == '\0' && "missing end of string"); - char* moduleName = (char*)tracy_malloc_fast(moduleNameSize); - memcpy(moduleName, ptrToData, moduleNameSize); + assert( (char)(ptrToData[moduleNameSize - 1]) == '\0' && "missing end of string" ); + char* moduleName = (char*)tracy_malloc_fast( moduleNameSize ); + memcpy( moduleName, ptrToData, moduleNameSize ); ptrToData += moduleNameSize; - StringLocation moduleNameStringLocation = StoreString(moduleName); + StringLocation moduleNameStringLocation = StoreString( moduleName, strlen( moduleName ) ); - uint32_t modulePathSize = MemRead(ptrToData); + uint32_t modulePathSize = MemRead( ptrToData ); ptrToData += sizeof(modulePathSize); - assert((char)(ptrToData[modulePathSize - 1]) == '\0' && "missing end of string"); - char* modulePath = (char*)tracy_malloc_fast(modulePathSize); + assert( (char)(ptrToData[modulePathSize - 1]) == '\0' && "missing end of string" ); + char* modulePath = (char*)tracy_malloc_fast( modulePathSize ); memcpy( modulePath, ptrToData, modulePathSize ); ptrToData += modulePathSize; - StringLocation modulePathStringLocation = StoreString(modulePath); + StringLocation modulePathStringLocation = StoreString( modulePath , strlen( modulePath )); - DebugFormat debugFormat = MemRead( ptrToData ); - ptrToData += sizeof( DebugFormat ); + ImageDebugFormat debugFormat = MemRead( ptrToData ); + ptrToData += sizeof( ImageDebugFormat ); ImageEntry moduleCacheEntry = { @@ -8964,27 +8912,27 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) .name = moduleName, .path = modulePath, }; - moduleCacheEntry.degugModuleField.debugFormat = debugFormat; + moduleCacheEntry.imageDebugInfo.debugFormat = debugFormat; - if (debugFormat != DebugFormat::NoDebugFormat) + if ( debugFormat != ImageDebugFormat::NoDebugFormat ) { uint32_t debugFormatSize = MemRead( ptrToData ); ptrToData += sizeof( debugFormatSize ); - uint8_t* debugData = (uint8_t*)tracy_malloc(debugFormatSize); - memcpy(debugData, ptrToData, debugFormatSize ); + uint8_t* debugData = (uint8_t*)tracy_malloc( debugFormatSize ); + memcpy( debugData, ptrToData, debugFormatSize ); ptrToData += debugFormatSize; - DegugModuleField& degugModuleField = moduleCacheEntry.degugModuleField; + ImageDebugInfo& imageDebugInfo = moduleCacheEntry.imageDebugInfo; - degugModuleField.debugData = debugData; - degugModuleField.debugDataSize = debugFormatSize; + imageDebugInfo.debugData = debugData; + imageDebugInfo.debugDataSize = debugFormatSize; } - if (m_symbolConfig.m_attemptResolutionByWorker) - CacheModuleAndLoadExternal(moduleCacheEntry); + if( m_symbolConfig.m_attemptResolutionByWorker ) + CacheModuleAndLoadExternal( moduleCacheEntry ); break; } @@ -8995,5 +8943,4 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) FreePendingData(); } - -} \ No newline at end of file +} diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index dae63f0155..85bd0b3257 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -438,7 +438,8 @@ class Worker struct PendingDataPacket { PacketDataType type; - std::vector data; + size_t dataSize; + uint8_t* data; }; public: @@ -568,7 +569,6 @@ class Worker const char* GetSymbolCode( uint64_t sym, uint32_t& len ) const; uint64_t GetSymbolForAddress( uint64_t address ); uint64_t GetSymbolForAddress( uint64_t address, uint32_t& offset ); - SymbolLocation GetSymbolLocFromSysAdrees( uint64_t address); uint64_t GetInlineSymbolForAddress( uint64_t address ) const; bool HasInlineSymbolAddresses() const { return !m_data.codeSymbolMap.empty(); } @@ -695,7 +695,6 @@ class Worker void CacheSourceFiles(); StringLocation StoreString(const char* str, size_t sz); - StringLocation StoreString(const char* str); std::vector& GetPendingThreadHints() { return m_pendingThreadHints; } void ClearPendingThreadHints() { m_pendingThreadHints.clear(); } @@ -773,7 +772,7 @@ class Worker tracy_force_inline void ProcessCallstack(); tracy_force_inline void ProcessCallstackSample( const QueueCallstackSample& ev ); tracy_force_inline void ProcessCallstackSampleContextSwitch( const QueueCallstackSample& ev ); - tracy_force_inline void ProcessCallstackFrameSize(const QueueCallstackFrameSize& ev, uint32_t imageNameIdx); + tracy_force_inline void ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev, uint32_t imageNameIdx ); tracy_force_inline void ProcessCallstackFrameSize( const QueueCallstackFrameSize& ev ); tracy_force_inline void ProcessCallstackFrame( const QueueCallstackFrame& ev, bool querySymbols ); tracy_force_inline void ProcessCallstackFrame(uint32_t line, uint64_t symAddr, uint32_t symLen, uint32_t nitidx, uint32_t fitidx, bool querySymbols); @@ -910,14 +909,14 @@ class Worker void AddSingleString( const char* str, size_t sz ); void AddSingleStringFailure( const char* str, size_t sz ); void AddSecondString( const char* str, size_t sz ); - void AddDataPacket( const void* data, size_t size , PacketDataType dataType); + void AddDataPacket( const void* data, size_t size , PacketDataType dataType ); void AddExternalName( uint64_t ptr, const char* str, size_t sz ); void AddExternalThreadName( uint64_t ptr, const char* str, size_t sz ); void AddFrameImageData( const char* data, size_t sz ); void AddSymbolCode( uint64_t ptr, const char* data, size_t sz ); void AddSourceCode( uint32_t id, const char* data, size_t sz ); - void TryResolveCallStackIfNeeded(CallstackFrameId frameId); + void TryResolveCallStackIfNeeded( CallstackFrameId frameId ); tracy_force_inline void AddCallstackPayload( const char* data, size_t sz ); tracy_force_inline void AddCallstackAllocPayload( const char* data ); uint32_t MergeCallstacks( uint32_t first, uint32_t second ); From 595188e00da62a77eaddf672922b4d83739c08dd Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Mon, 10 Mar 2025 15:49:04 +0100 Subject: [PATCH 12/40] Fix lifetime of callstack symbol resolver (Init/EndCallstack) --- server/TracyWorker.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 20b43bfe67..e89b839fd5 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -283,6 +283,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb , m_traceVersion( CurrentVersion ) , m_loadTime( 0 ) { + InitCallstack(); m_data.sourceLocationExpand.push_back( 0 ); m_data.localThreadCompress.InitZero(); m_data.callstackPayload.push_back( nullptr ); @@ -324,6 +325,7 @@ Worker::Worker( const char* name, const char* program, const std::vector Date: Mon, 10 Mar 2025 16:15:05 +0100 Subject: [PATCH 13/40] Always save image infos so that we may resolve symbols later on --- public/common/TracyCallstack.cpp | 6 +++--- public/common/TracyCallstack.hpp | 3 +-- server/TracyWorker.cpp | 6 ++---- 3 files changed, 6 insertions(+), 9 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 2d6b6577bc..8b0ccbf718 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -947,7 +947,7 @@ bool LoadFromPdb( const char* moduleName, uint64_t baseAddress, uint64_t dllSize } // Called from the profiler (server) only, we received data from the client. -void CacheModuleAndLoadExternal( ImageEntry& imageEntry ) +void CacheImageAndLoadDebugInfo( ImageEntry& imageEntry, bool loadDebugInfo ) { if ( IsKernelAddress( imageEntry.start ) ) { @@ -1195,7 +1195,7 @@ int cb_num; CallstackEntry cb_data[MaxCbTrace]; int cb_fixup; -void CacheModuleAndLoadExternal( ImageEntry& ImageEntry ) {} +void CacheImageAndLoadDebugInfo( ImageEntry& imageEntry, bool loadDebugInfo ) {} #ifdef TRACY_DEBUGINFOD debuginfod_client* s_debuginfod; @@ -1733,7 +1733,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ #elif TRACY_HAS_CALLSTACK == 5 -void CacheModuleAndLoadExternal( ImageEntry& ImageEntry ) {} +void CacheImageAndLoadDebugInfo( ImageEntry& imageEntry, bool loadDebugInfo ) {} void InitCallstackCritical() { diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index 209d5a2edc..8e8750f85b 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -115,8 +115,7 @@ void InitCallstackCritical(); void EndCallstack(); const char* GetKernelModulePath( uint64_t addr ); - -void CacheModuleAndLoadExternal(ImageEntry& moduleCacheEntry); +void CacheImageAndLoadDebugInfo( ImageEntry& imageEntry, bool loadDebugInfo ); const FastVector* GetUserImageInfos(); const FastVector* GetKernelImageInfos(); diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index e89b839fd5..a34d437131 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -1702,8 +1702,7 @@ Worker::Worker( FileRead& f, const SymbolResolutionConfig& symbolResConfig, Even DeserializeDebugField( &imageEntry.imageDebugInfo ); - if( m_symbolConfig.m_attemptResolutionByWorker ) - CacheModuleAndLoadExternal( imageEntry ); + CacheImageAndLoadDebugInfo( imageEntry, m_symbolConfig.m_attemptResolutionByWorker ); } }; @@ -8930,8 +8929,7 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) } - if( m_symbolConfig.m_attemptResolutionByWorker ) - CacheModuleAndLoadExternal( moduleCacheEntry ); + CacheImageAndLoadDebugInfo( moduleCacheEntry, m_symbolConfig.m_attemptResolutionByWorker ); break; } From 49880f9937abfafe4259749ab088dc90c8d30dfd Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Mon, 10 Mar 2025 16:23:08 +0100 Subject: [PATCH 14/40] Fix TryResolveCallstack when worker does not allow to query symbols --- server/TracyWorker.cpp | 50 +++++++++++++++++++++++------------------- server/TracyWorker.hpp | 2 +- 2 files changed, 29 insertions(+), 23 deletions(-) diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index a34d437131..e0d5a4c145 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -4048,7 +4048,7 @@ uint64_t Worker::GetCanonicalPointer( const CallstackFrameId& id ) const return ( id.idx & 0x3FFFFFFFFFFFFFFF ) | ( ( id.idx & 0x3000000000000000 ) << 2 ); } -void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId ) +void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySymbols ) { // If we already have the information if (m_data.callstackFrameMap.count( frameId ) != 0) @@ -4076,12 +4076,11 @@ void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId ) m_pendingCallstackFrames++; ProcessCallstackFrameSize( queueCallstackFrameSize, imageNameIdx ); - // we know that for the best we have inline information for (size_t i = 0; i < queueCallstackFrameSize.size; i++) { const auto& frame = outCallStack.data[i]; ProcessCallstackFrame( frame.line, frame.symAddr, frame.symLen, - StoreString( frame.name, strlen( frame.name ) ).idx, StoreString( frame.file, strlen( frame.file ) ).idx, true ); + StoreString( frame.name, strlen( frame.name ) ).idx, StoreString( frame.file, strlen( frame.file ) ).idx, querySymbols ); // We copied it, now free the content tracy_free_fast( (void*)frame.name ); @@ -4091,27 +4090,34 @@ void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId ) assert( m_pendingCallstackSubframes == 0 ); } } + else + { + status = DecodeCallStackPtrStatus::SymbolMissing; + } - switch (status) + if(querySymbols) { - break; - case tracy::DecodeCallStackPtrStatus::ModuleMissing: - // TODO: actually only query module info, and then try to resolve again locally. - // Or we could just rely on the user triggering a new symbol resolution manually. - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, symbolAddress ); - break; - case tracy::DecodeCallStackPtrStatus::Count: - assert( false && "Missing error handling ?" ); - [[fallthrough]]; - case tracy::DecodeCallStackPtrStatus::SymbolMissing: - // Fallback to asking the client for the symbol since we couldn't find it. - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, symbolAddress ); - break; - case tracy::DecodeCallStackPtrStatus::Success: - default: - break; + switch (status) + { + break; + case DecodeCallStackPtrStatus::ModuleMissing: + // TODO: actually only query module info, and then try to resolve again locally. + // Or we could just rely on the user triggering a new symbol resolution manually. + m_pendingCallstackFrames++; + Query( ServerQueryCallstackFrame, symbolAddress ); + break; + case DecodeCallStackPtrStatus::Count: + assert( false && "Missing error handling ?" ); + [[fallthrough]]; + case DecodeCallStackPtrStatus::SymbolMissing: + // Fallback to asking the client for the symbol since we couldn't find it. + m_pendingCallstackFrames++; + Query( ServerQueryCallstackFrame, symbolAddress ); + break; + case tracy::DecodeCallStackPtrStatus::Success: + default: + break; + } } } diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 85bd0b3257..bea30c576d 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -916,7 +916,7 @@ class Worker void AddSymbolCode( uint64_t ptr, const char* data, size_t sz ); void AddSourceCode( uint32_t id, const char* data, size_t sz ); - void TryResolveCallStackIfNeeded( CallstackFrameId frameId ); + void TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySymbols = true ); tracy_force_inline void AddCallstackPayload( const char* data, size_t sz ); tracy_force_inline void AddCallstackAllocPayload( const char* data ); uint32_t MergeCallstacks( uint32_t first, uint32_t second ); From 140a33d1705dc1087b47c9faeddb07c0d9892bda Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Mon, 10 Mar 2025 16:43:49 +0100 Subject: [PATCH 15/40] Warn once about symsrv if symbol resolution fails --- public/common/TracyCallstack.cpp | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 8b0ccbf718..eb8f0f2c06 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -1106,7 +1106,20 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ const auto symValid = SymFromAddr( proc, ptr, nullptr, si ) != 0; if( symValid ) *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; - else *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::SymbolMissing; + else { + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::SymbolMissing; +#ifdef TRACY_VERBOSE + static bool doOnce = true; + if(doOnce) + { + if( GetModuleHandleA( "symsrv.dll" ) == NULL ) + { + TracyDebug( "symsrv.dll was not loaded. Symbol resolution may fail.\n" ); + } + doOnce = false; + } +#endif + } IMAGEHLP_LINE64 line; From 43faca0038bc2365fed0f911b591f78395869bb0 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Mon, 10 Mar 2025 16:23:31 +0100 Subject: [PATCH 16/40] Resolve Symbols button --- profiler/src/profiler/TracyView.cpp | 12 ++++++++ server/TracyWorker.cpp | 47 +++++++++++++++++++++++++++++ server/TracyWorker.hpp | 1 + 3 files changed, 60 insertions(+) diff --git a/profiler/src/profiler/TracyView.cpp b/profiler/src/profiler/TracyView.cpp index ad3de94557..fc04bcc194 100644 --- a/profiler/src/profiler/TracyView.cpp +++ b/profiler/src/profiler/TracyView.cpp @@ -913,6 +913,18 @@ bool View::DrawImpl() ImGui::SameLine(); ToggleButton( ICON_FA_FINGERPRINT " Info", m_showInfo ); ImGui::SameLine(); + + if( m_filename.empty() ) // Can't modify when loading from file + { + ImGui::SameLine(); + if (ImGui::Button( ICON_FA_USER_SECRET " Resolve Symbols")) + { + // TODO: Make it async ? + m_worker.ResolveSymbolLocally(); + } + + } + if( ImGui::Button( ICON_FA_SCREWDRIVER_WRENCH ) ) ImGui::OpenPopup( "ToolsPopup" ); if( ImGui::BeginPopup( "ToolsPopup" ) ) { diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index e0d5a4c145..81a6193b39 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -5223,6 +5223,53 @@ void Worker::SourceLocationOverflowFailure() m_failure = Failure::SourceLocationOverflow; } +void Worker::ResolveSymbolLocal() +{ + const char * unresolvedStr = "[unresolved]"; + StringIdx unresolvedStrIdx = StoreString(unresolvedStr, strlen(unresolvedStr) ).idx; + + if(!m_symbolConfig.m_attemptResolutionByWorker) + { + // TODO: Load debug info. + } + + for (auto& it : m_data.callstackFrameMap) + { + CallstackFrameData& toResolveData = *it.second; + if(toResolveData.data[0].name.Idx() == unresolvedStrIdx.Idx()) + { + uint64_t symbolAddress = GetCanonicalPointer( it.first ); + DecodeCallStackPtrStatus status; + CallstackEntryData outCallStack = DecodeCallstackPtr( symbolAddress, &status ); + if ( status == DecodeCallStackPtrStatus::Success ) + { + assert( outCallStack.size <= 255 ); + assert( toResolveData.imageName.Idx() == StoreString( outCallStack.imageName, strlen(outCallStack.imageName)).idx); + if( toResolveData.size != outCallStack.size ) + { + // We're "leaking" the unresolved data until slab is reset + toResolveData.data = m_slab.Alloc( outCallStack.size ); + } + + for( size_t idx=0; idx Date: Thu, 13 Mar 2025 12:16:02 +0100 Subject: [PATCH 17/40] Self Profiling working --- profiler/CMakeLists.txt | 4 +++- server/TracyWorker.cpp | 19 ++++++++++++++++++- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/profiler/CMakeLists.txt b/profiler/CMakeLists.txt index a9f1d3d00a..41eabb865a 100644 --- a/profiler/CMakeLists.txt +++ b/profiler/CMakeLists.txt @@ -19,7 +19,9 @@ project( if(SELF_PROFILE) add_definitions(-DTRACY_ENABLE) - add_compile_options(-g -O3 -fno-omit-frame-pointer) + if(NOT MSVC) + add_compile_options(-g -O3 -fno-omit-frame-pointer) + endif() endif() include(${CMAKE_CURRENT_LIST_DIR}/../cmake/config.cmake) diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 81a6193b39..46e3d16517 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -45,6 +45,10 @@ #include "TracyWorker.hpp" #include "tracy_pdqsort.h" +#ifdef TRACY_ENABLE // The worker should only have TRACY_ENABLE when self profiling +#define TRACY_SELF_PROFILE +#endif + namespace tracy { @@ -283,7 +287,9 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb , m_traceVersion( CurrentVersion ) , m_loadTime( 0 ) { +#ifndef TRACY_SELF_PROFILE // Self profiling will use the old path and query symbols InitCallstack(); +#endif m_data.sourceLocationExpand.push_back( 0 ); m_data.localThreadCompress.InitZero(); m_data.callstackPayload.push_back( nullptr ); @@ -325,7 +331,9 @@ Worker::Worker( const char* name, const char* program, const std::vector Date: Thu, 13 Mar 2025 15:28:34 +0100 Subject: [PATCH 18/40] Update tracy's document for Server symbol resolution --- manual/tracy.tex | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/manual/tracy.tex b/manual/tracy.tex index 29633c7906..a3baac3715 100644 --- a/manual/tracy.tex +++ b/manual/tracy.tex @@ -1895,10 +1895,32 @@ \subsubsection{Debugging symbols} At initilization time, tracy will attempt to preload symbols for device drivers and process modules. As this process can be slow when a lot of pdbs are involved, you can set the \texttt{TRACY\_NO\_DBGHELP\_INIT\_LOAD} environment variable to "1" to disable this behavior and rely on-demand symbol loading. +\begin{bclogo}[ +noborder=true, +couleur=black!5, +logo=\bcbombe +]{Important} + +To be able to fully resolve kernel symbols, you might need to have matching versions of \texttt{dbghelp.dll} and \texttt{symsrv.dll} in your \texttt{PATH} or working directory and use symbol servers. Set the \texttt{\_NT\_SYMBOL\_PATH} environment variable to point to the symbol servers you want. For example: +\begin{lstlisting} +_NT\_SYMBOL\_PATH=srv*YourPdbCachePath*https://msdl.microsoft.com/download/symbols +\end{lstlisting} + +To get more information about this topic, check Microsoft's official docs: \url{https://learn.microsoft.com/en-us/windows-hardware/drivers/debugger/symbol-path}. +Note that downloading the kernel drivers symbols can be slow at the first execution of the program, or a Window update. +\end{bclogo} + \paragraph{Disabling resolution of inline frames} Inline frames retrieval on Windows can be multiple orders of magnitude slower than just performing essential symbol resolution. This manifests as profiler seemingly being stuck for a long time, having hundreds of thousands of query backlog entries queued, which are slowly trickling down. If your use case requires speed of operation rather than having call stacks with inline frames included, you may define the \texttt{TRACY\_NO\_CALLSTACK\_INLINES} macro, which will make the profiler stick to the basic but fast frame resolution mode. +\paragraph{Server symbol resolution} + +Instead of having the client do the resolution, you can configure the server to attempt the symbol resolution. +This is controlled by the profiler global settings in the "Symbol resolution" section. +If "Attempt resolution by profiler" is enabled, the server will attempt to resolve symbols locally. For this, the Tracy-Client will send informations about the executable images loaded by the process. If symbol resolution on the server fails, it will query the client for the information as a fallback mechanism. +However if "Prevent resolution by application" is enabled, Tracy-Client will not resolve any symbols and simply send the minimal images information. + \paragraph{Offline symbol resolution} By default, tracy client resolves callstack symbols in a background thread at runtime. From b185d37d2cf7e8323faf4a746cd9033083305be3 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Fri, 14 Mar 2025 11:45:34 +0100 Subject: [PATCH 19/40] Fix grammar issue --- public/client/TracyProfiler.cpp | 7 +++---- public/client/TracyProfiler.hpp | 2 +- server/TracyWorker.cpp | 8 ++++---- server/TracyWorker.hpp | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index f8d4d56188..7b8a6dbdee 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -1994,7 +1994,7 @@ void Profiler::Worker() m_deferredLock.unlock(); #endif // Send all known modules information. - SendModulesCaches(); + SendCachedModulesInformation(); // Main communications loop int keepAlive = 0; @@ -4076,7 +4076,7 @@ void Profiler::ReportTopology() # endif } -void Profiler::SendModulesCaches() +void Profiler::SendCachedModulesInformation() { #ifdef TRACY_HAS_CALLSTACK // We are retrieving modules information from the Tracy Profiler thread @@ -4120,7 +4120,6 @@ void Profiler::WriteDebugFieldToPacket( uint8_t** ptr, int* currentPacketSize, c memcpy( *ptr, imageDebugInfo.debugData, dataSize ); *ptr += dataSize; *currentPacketSize += dataSize; - } void Profiler::SendModuleInfo( const ImageEntry& imageEntry ) @@ -4182,7 +4181,7 @@ void Profiler::SendModuleInfo( const ImageEntry& imageEntry ) QueueItem item2; tracy::MemWrite( &item2.hdr.type, (int)QueueType::ModuleUpdate ); - static_assert( tracy::QueueDataSize[(int)QueueType::ModuleUpdate] == sizeof( QueueItem ), "Size missmatch" ); + static_assert( tracy::QueueDataSize[(int)QueueType::ModuleUpdate] == sizeof( QueueItem ), "Size mismatch" ); NeedDataSize( sizeof( item2 ) ); AppendData( &item2, sizeof( item2 ) ); diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index f5fd1c955e..b3deaa2a65 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -919,7 +919,7 @@ class Profiler void CalibrateDelay(); void ReportTopology(); - void SendModulesCaches(); + void SendCachedModulesInformation(); #ifdef TRACY_HAS_CALLSTACK void WriteDebugFieldToPacket( uint8_t** ptr, int* currentPacketSize, const ImageDebugInfo& imageDebugInfo ); diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 46e3d16517..00cad06a5a 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -1697,7 +1697,7 @@ Worker::Worker( FileRead& f, const SymbolResolutionConfig& symbolResConfig, Even f.Read( *s, sLength ); }; - auto DeseriliazeImageEntries = [&]() + auto DeserializeImageEntries = [&]() { uint32_t moduleCount = 0; f.Read( moduleCount ); @@ -1718,8 +1718,8 @@ Worker::Worker( FileRead& f, const SymbolResolutionConfig& symbolResConfig, Even if ( CurrentVersion >= FileVersion( 0, 11, 3 ) ) { - DeseriliazeImageEntries(); // UserLandModules - DeseriliazeImageEntries(); // KernelModules + DeserializeImageEntries(); // UserLandModules + DeserializeImageEntries(); // KernelModules } // Post read processing @@ -5238,7 +5238,7 @@ void Worker::SourceLocationOverflowFailure() m_failure = Failure::SourceLocationOverflow; } -void Worker::ResolveSymbolLocal() +void Worker::ResolveSymbolLocally() { #ifndef TRACY_SELF_PROFILE // Would be racy by design when self profiling, need to offload this to the Symbol Worker const char * unresolvedStr = "[unresolved]"; diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 5645cb4e4a..daa03b9572 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -691,7 +691,7 @@ class Worker void DoPostponedInlineSymbols(); void DoPostponedWork(); void DoPostponedWorkAll(); - void ResolveSymbolLocal(); + void ResolveSymbolLocally(); void CacheSourceFiles(); From c6f84877bf11e2e6ea8a6383c90d00e19b826c1c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Tue, 18 Mar 2025 22:04:38 +0100 Subject: [PATCH 20/40] WriteDebugFieldToPacket is now a static function --- public/client/TracyProfiler.cpp | 2 +- public/client/TracyProfiler.hpp | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 7b8a6dbdee..539ba2e6d4 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -4105,7 +4105,7 @@ void Profiler::SendCachedModulesInformation() #ifdef TRACY_HAS_CALLSTACK -void Profiler::WriteDebugFieldToPacket( uint8_t** ptr, int* currentPacketSize, const ImageDebugInfo& imageDebugInfo ) +static void WriteDebugFieldToPacket( uint8_t** ptr, int* currentPacketSize, const ImageDebugInfo& imageDebugInfo ) { MemWrite( *ptr, imageDebugInfo.debugFormat ); *ptr += sizeof( imageDebugInfo.debugFormat ); diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index b3deaa2a65..ebc7564615 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -922,7 +922,6 @@ class Profiler void SendCachedModulesInformation(); #ifdef TRACY_HAS_CALLSTACK - void WriteDebugFieldToPacket( uint8_t** ptr, int* currentPacketSize, const ImageDebugInfo& imageDebugInfo ); void SendModuleInfo( const ImageEntry& moduleCacheEntry ); #endif From 676ed7a4b0c5f85506d5ce832944df0e05c5e99a Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Fri, 14 Mar 2025 11:45:56 +0100 Subject: [PATCH 21/40] modify some comment --- public/common/TracyCallstack.cpp | 2 +- public/common/TracyQueue.hpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index eb8f0f2c06..cac8119497 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -1061,7 +1061,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ #ifdef TRACY_DBGHELP_LOCK DBGHELP_UNLOCK; #endif - // may use symLen for base adress + cb_data[0].symAddr = ptr - moduleNameAndAddress.baseAddr; cb_data[0].symLen = 0; diff --git a/public/common/TracyQueue.hpp b/public/common/TracyQueue.hpp index e41fae056f..b6dfe40946 100644 --- a/public/common/TracyQueue.hpp +++ b/public/common/TracyQueue.hpp @@ -876,7 +876,7 @@ static constexpr size_t QueueDataSize[] = { sizeof( QueueHeader ) + sizeof( QueueFiberEnter ), sizeof( QueueHeader ) + sizeof( QueueFiberLeave ), // above items must be first - sizeof( QueueItem ), // client modules base address, + sizeof( QueueItem ), // image datas, sizeof( QueueHeader ) + sizeof( QueueDataPacket ), // DataPacket sizeof( QueueHeader ), // terminate sizeof( QueueHeader ), // keep alive From 62f0f592e8186c528834d33131e8cf8a85059ae6 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Mon, 17 Mar 2025 12:14:22 +0100 Subject: [PATCH 22/40] Add get build_id while dl_iterate_phdr --- public/common/TracyAlign.hpp | 6 ++ public/common/TracyCallstack.cpp | 102 ++++++++++++++++++++++++++----- public/common/TracyCallstack.hpp | 7 ++- public/libbacktrace/elf.cpp | 2 +- server/TracyWorker.cpp | 12 ++-- 5 files changed, 103 insertions(+), 26 deletions(-) diff --git a/public/common/TracyAlign.hpp b/public/common/TracyAlign.hpp index c3531ba0dd..6422f941a8 100644 --- a/public/common/TracyAlign.hpp +++ b/public/common/TracyAlign.hpp @@ -22,6 +22,12 @@ tracy_force_inline void MemWrite( void* ptr, T val ) memcpy( ptr, &val, sizeof( T ) ); } +template +inline T Align( T val, size_t align ) +{ + return (((val) + (T)(align) - 1) & ~(T)((align) - 1)); +} + } #endif diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index cac8119497..e0e6d606b4 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -1,10 +1,14 @@ +#include #include #include #include #include // for memcpy #include +#include +#include #include "TracyCallstack.hpp" +#include "TracyAlign.hpp" #include "TracyDebug.hpp" #include "TracyStringHelpers.hpp" #include "TracyAlloc.hpp" @@ -112,6 +116,12 @@ extern "C" const char* ___tracy_demangle( const char* mangled ) #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_USE_IMAGE_CACHE # include +struct BuildIdNote { + ElfW(Nhdr) nhdr; + + char name[4]; + uint8_t build_id[0]; +}; #endif namespace tracy @@ -150,7 +160,20 @@ namespace tracy class ImageCache { public: - ImageCache( size_t moduleCacheCapacity = 512 ) : m_modCache( moduleCacheCapacity ) {} + ImageCache( size_t moduleCacheCapacity = 512 ) : m_modCache( moduleCacheCapacity ) + { + for (auto& it : m_modCache) + { + it.start = 0; + it.end = 0; + it.name = nullptr; + it.path = nullptr; + + it.imageDebugInfo.debugData = nullptr; + it.imageDebugInfo.debugDataSize = 0; + it.imageDebugInfo.debugFormat = ImageDebugFormatId::NoDebugFormat; + } + } ~ImageCache() { Clear(); } ImageEntry* CacheModuleWithDebugInfo( const ImageEntry& entry ) @@ -225,6 +248,8 @@ namespace tracy // 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 ImageCacheLibbacktrace : public ImageCache { public: @@ -255,6 +280,38 @@ class ImageCacheLibbacktrace : public ImageCache bool m_updated = false; bool m_haveMainImageName = false; + // success return 0 + // failed return 1 + static int GetBuildIdNoteFromNote( ImageEntry* imageEntry, BuildIdNote* note, ptrdiff_t fileLen) + { + auto& nhdr = note->nhdr; + + while( fileLen >= sizeof(BuildIdNote) ) + { + if (nhdr.n_type == NT_GNU_BUILD_ID && + nhdr.n_descsz != 0 && + nhdr.n_namesz == 4 && + memcmp(note->name, "GNU", 4) == 0) + { + imageEntry->imageDebugInfo.debugFormat = ImageDebugFormatId::ElfDebugFormat; + imageEntry->imageDebugInfo.debugDataSize = nhdr.n_descsz; + imageEntry->imageDebugInfo.debugData = (uint8_t*)tracy_malloc(imageEntry->imageDebugInfo.debugDataSize); + memcpy(imageEntry->imageDebugInfo.debugData, ¬e->build_id[0], imageEntry->imageDebugInfo.debugDataSize); + + return 0; + } + + const size_t offset = sizeof(ElfW(Nhdr)) + + tracy::Align(note->nhdr.n_namesz, 4) + + tracy::Align(note->nhdr.n_descsz, 4); + + note = reinterpret_cast((char *)note + offset); + fileLen -= offset; + } + + return 1; + } + static int Callback( struct dl_phdr_info* info, size_t size, void* data ) { ImageCacheLibbacktrace* cache = reinterpret_cast( data ); @@ -270,18 +327,28 @@ class ImageCacheLibbacktrace : public ImageCache ImageEntry image{}; image.start = startAddress; image.end = 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 - if( info->dlpi_name && info->dlpi_name[0] != '\0' ) - { - image.name = CopyStringFast(info->dlpi_name); - } - else + image.name = info->dlpi_name && info->dlpi_name[0] != '\0' ? CopyStringFast(info->dlpi_name) : nullptr; + + + for (unsigned i = 0; i < info->dlpi_phnum; i++) { - image.name = nullptr; - } + if (info->dlpi_phdr[i].p_type != PT_NOTE) + continue; + + void* raw = (void*)(info->dlpi_addr + + info->dlpi_phdr[i].p_vaddr); + + ptrdiff_t len = info->dlpi_phdr[i].p_filesz; + + if (GetBuildIdNoteFromNote(&image, static_cast(raw), len) == 0) + break; + + } + cache->CacheModuleWithDebugInfo(image); cache->m_updated = true; @@ -358,6 +425,8 @@ 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() @@ -436,7 +505,7 @@ struct CV_INFO_PDB70 static constexpr DWORD CV_SIGNATURE_RSDS = 'SDSR'; // 'SDSR' -bool GetModuleInfoFromPEHeaders( uint64_t baseOfDll, ImageDebugFormat* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize ) +bool GetModuleInfoFromPEHeaders( uint64_t baseOfDll, ImageDebugFormatId* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize ) { static constexpr bool MappedAsImage = true; @@ -471,7 +540,7 @@ bool GetModuleInfoFromPEHeaders( uint64_t baseOfDll, ImageDebugFormat* debugForm if( pData->CvSignature != CV_SIGNATURE_RSDS ) continue; - *debugFormat = ImageDebugFormat::PdbDebugFormat; + *debugFormat = ImageDebugFormatId::PdbDebugFormat; const uint32_t pdbFileLength = strlen( (const char*)pData->PdbFileName ); const uint32_t debugFormatSize = sizeof( PEImageDebugData ) + pdbFileLength + 1; @@ -519,7 +588,7 @@ void GetModuleInfoFromDbgHelp( const char* imageName, ImageEntry* moduleEntry ) return; ImageDebugInfo& debugInfo = moduleEntry->imageDebugInfo; - debugInfo.debugFormat = ImageDebugFormat::PdbDebugFormat; + debugInfo.debugFormat = ImageDebugFormatId::PdbDebugFormat; const uint32_t pdbFileNameLen = static_cast( strlen( moduleInfo.CVData ) ); debugInfo.debugDataSize = sizeof( PEImageDebugData ) + ( pdbFileNameLen + 1 ); @@ -552,7 +621,7 @@ ImageEntry* CacheModuleInfo( const char* imageName, uint32_t imageNameLength, ui moduleEntry.path = CopyStringFast( imageName, imageNameLength ); FormatImageName( &moduleEntry.name, imageName, imageNameLength ); - ImageDebugFormat debugFormat = ImageDebugFormat::NoDebugFormat; + ImageDebugFormatId debugFormat = ImageDebugFormatId::NoDebugFormat; uint8_t* debugData = nullptr; uint32_t debugDataSize = 0; @@ -881,9 +950,9 @@ const char* GetKernelModulePath( uint64_t addr ) return nullptr; } -bool LoadFromPdb( const char* moduleName, uint64_t baseAddress, uint64_t dllSize, ImageDebugFormat debugFormat, const uint8_t* debugData, uint32_t debugDataSize ) +bool LoadFromPdb( const char* moduleName, uint64_t baseAddress, uint64_t dllSize, ImageDebugFormatId debugFormat, const uint8_t* debugData, uint32_t debugDataSize ) { - assert( debugFormat == ImageDebugFormat::PdbDebugFormat ); + assert( debugFormat == ImageDebugFormatId::PdbDebugFormat ); const uint32_t DataForDebugSize = static_cast( debugDataSize ); assert( moduleName != nullptr ); @@ -959,7 +1028,7 @@ void CacheImageAndLoadDebugInfo( ImageEntry& imageEntry, bool loadDebugInfo ) } bool hasSymbolInfo = false; - if( imageEntry.imageDebugInfo.debugFormat == ImageDebugFormat::PdbDebugFormat ) + if( imageEntry.imageDebugInfo.debugFormat == ImageDebugFormatId::PdbDebugFormat ) { char* nameFixed = nullptr; if( IsKernelAddress( imageEntry.start ) ) @@ -1221,6 +1290,7 @@ struct DebugInfo int fd; }; + static FastVector* s_di_known; #endif diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index 8e8750f85b..c84450ee2f 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -53,11 +53,12 @@ enum struct DecodeCallStackPtrStatus Count }; -enum struct ImageDebugFormat : uint8_t +enum struct ImageDebugFormatId : uint8_t { NoDebugFormat, PdbDebugFormat, - DwarfDebugFormat, + GNUDebugFormat, + ElfDebugFormat }; struct CallstackSymbolData @@ -86,7 +87,7 @@ struct CallstackEntryData struct ImageDebugInfo { - ImageDebugFormat debugFormat; + ImageDebugFormatId debugFormat; uint8_t* debugData; uint32_t debugDataSize; }; diff --git a/public/libbacktrace/elf.cpp b/public/libbacktrace/elf.cpp index c1a438b72d..6234821c20 100644 --- a/public/libbacktrace/elf.cpp +++ b/public/libbacktrace/elf.cpp @@ -75,7 +75,7 @@ namespace tracy { #ifdef TRACY_DEBUGINFOD -int GetDebugInfoDescriptor( const char* buildid_data, size_t buildid_size ); +int GetDebugInfoDescriptor( const char* buildid_data, size_t buildid_size , const char* file_name ); #endif #if !defined(HAVE_DECL_STRNLEN) || !HAVE_DECL_STRNLEN diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 00cad06a5a..d74d70841a 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -1667,10 +1667,10 @@ Worker::Worker( FileRead& f, const SymbolResolutionConfig& symbolResConfig, Even auto DeserializeDebugField = [&]( ImageDebugInfo* debugField ) { - ImageDebugFormat debugFormat; + ImageDebugFormatId debugFormat; f.Read( debugFormat ); - if( debugFormat == ImageDebugFormat::NoDebugFormat ) + if( debugFormat == ImageDebugFormatId::NoDebugFormat ) { debugField->debugDataSize = 0; debugField->debugData = nullptr; @@ -8646,7 +8646,7 @@ void Worker::Write( FileWrite& f, bool fiDict ) auto SerializeDebugModuleField = [&]( const ImageDebugInfo& debugField ) { f.Write( &debugField.debugFormat, sizeof( debugField.debugFormat ) ); - if( debugField.debugFormat == ImageDebugFormat::NoDebugFormat ) + if( debugField.debugFormat == ImageDebugFormatId::NoDebugFormat ) return; f.Write( &debugField.debugDataSize, sizeof( debugField.debugDataSize ) ); @@ -8970,8 +8970,8 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) StringLocation modulePathStringLocation = StoreString( modulePath , strlen( modulePath )); - ImageDebugFormat debugFormat = MemRead( ptrToData ); - ptrToData += sizeof( ImageDebugFormat ); + ImageDebugFormatId debugFormat = MemRead( ptrToData ); + ptrToData += sizeof( ImageDebugFormatId ); ImageEntry moduleCacheEntry = { @@ -8982,7 +8982,7 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) }; moduleCacheEntry.imageDebugInfo.debugFormat = debugFormat; - if ( debugFormat != ImageDebugFormat::NoDebugFormat ) + if ( debugFormat != ImageDebugFormatId::NoDebugFormat ) { uint32_t debugFormatSize = MemRead( ptrToData ); From 2992339f091d9478d4eb376abecf39d9a72eaaca Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Wed, 19 Mar 2025 17:43:11 +0100 Subject: [PATCH 23/40] Add Sending Image Data from backend when found new image --- public/client/TracyProfiler.cpp | 226 +++++++++++++++++-------------- public/client/TracyProfiler.hpp | 4 +- public/common/TracyCallstack.cpp | 29 ++-- public/common/TracyCallstack.hpp | 15 +- public/common/TracyProtocol.hpp | 1 - public/common/TracyQueue.hpp | 16 +-- server/TracyWorker.cpp | 154 +++++++++------------ server/TracyWorker.hpp | 5 +- 8 files changed, 228 insertions(+), 222 deletions(-) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 539ba2e6d4..6bd76e9022 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -2682,6 +2682,13 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token ) ++item; continue; } + case QueueType::ImageUpdate: + { + void* ptr = (void*)item->imageEntry.payload; + SendSingleDataPacket( ptr, item->imageEntry.payloadSize ); + tracy_free( ptr ); + break; + } default: assert( false ); break; @@ -3260,22 +3267,25 @@ void Profiler::SendLongString( uint64_t str, const char* ptr, size_t len, QueueT AppendDataUnsafe( ptr, l32 ); } -void Profiler::SendSingleDataPacket( void* ptr, size_t totalSize, PacketDataType type ) +void Profiler::SendSingleDataPacket( void* ptr, size_t totalSize ) { - assert( totalSize <= std::numeric_limits::max() ); + assert(totalSize <= std::numeric_limits::max()); - static_assert( sizeof( QueueHeader ) + sizeof( QueueDataPacket ) == QueueDataSize[(int)QueueType::DataPacket], "Size mismatch" ); + static_assert(sizeof(QueueHeader) + sizeof(QueueDataPacket) == QueueDataSize[(int)QueueType::DataPacket], "Size mismatch"); - NeedDataSize( QueueDataSize[(int)QueueType::DataPacket] + totalSize ); - QueueItem item; - tracy::MemWrite( &item.hdr.type, (int)QueueType::DataPacket ); - tracy::MemWrite( &item.packet.packetDataType, type ); - uint16_t dataSize = uint16_t( totalSize ); - tracy::MemWrite( &item.packet.packetSize, dataSize ); + NeedDataSize(QueueDataSize[(int)QueueType::DataPacket] + totalSize); + + QueueItem item; + tracy::MemWrite(&item.hdr.type, (int)QueueType::DataPacket); - AppendDataUnsafe( &item, QueueDataSize[(int)QueueType::DataPacket] ); - AppendDataUnsafe( ptr, dataSize ); + uint16_t dataSize = uint16_t(totalSize); + tracy::MemWrite(&item.packet.packetSize, dataSize); + + AppendDataUnsafe(&item, QueueDataSize[(int)QueueType::DataPacket]); + AppendDataUnsafe(ptr, dataSize); + + } void Profiler::SendSourceLocation( uint64_t ptr ) @@ -3441,14 +3451,114 @@ void Profiler::QueueSourceCodeQuery( uint32_t id ) } #ifdef TRACY_HAS_CALLSTACK + +static void WriteDebugFieldToPacket( uint8_t** ptr, const ImageDebugInfo& imageDebugInfo ) +{ + MemWrite( *ptr, imageDebugInfo.debugFormat ); + *ptr += sizeof( imageDebugInfo.debugFormat ); + + const uint32_t dataSize = static_cast( imageDebugInfo.debugDataSize ); + + MemWrite( *ptr, dataSize ); + *ptr += sizeof( dataSize ); + + memcpy( *ptr, imageDebugInfo.debugData, dataSize ); + *ptr += dataSize; +} + + +static void SerializeImageEntry( const ImageEntry& imageEntry, void** outptr, size_t* outsize ) +{ + static constexpr int EndOfString = 1; + + const uint32_t moduleNameLength = ( imageEntry.name ? strlen( imageEntry.name ) : 0 ) + EndOfString; + const uint32_t modulePathLength = ( imageEntry.path ? strlen( imageEntry.path ) : 0 ) + EndOfString; + + + const size_t baseModuleInfo = sizeof( imageEntry.start ) + sizeof( imageEntry.end ) + + sizeof( moduleNameLength ) + moduleNameLength + + sizeof( modulePathLength ) + modulePathLength + + sizeof( imageEntry.imageDebugInfo.debugFormat ); + + const uint32_t debugFormatSize = 0 + sizeof( uint32_t ) + + static_cast( imageEntry.imageDebugInfo.debugDataSize ); + + const size_t bufferSize = baseModuleInfo + debugFormatSize; + void* queueBuffer = tracy_malloc( bufferSize ); + + uint8_t* ptr = (uint8_t*)queueBuffer; + + MemWrite( ptr, imageEntry.start ); + ptr += sizeof( imageEntry.start ); + + MemWrite( ptr, imageEntry.end ); + ptr += sizeof( imageEntry.end ); + + MemWrite( ptr, moduleNameLength ); + ptr += sizeof( moduleNameLength ); + + memcpy( ptr, imageEntry.name ? imageEntry.name : "", moduleNameLength ); + ptr += moduleNameLength; + + MemWrite( ptr, modulePathLength ); + ptr += sizeof( modulePathLength ); + + memcpy( ptr, imageEntry.path ? imageEntry.path : "", modulePathLength ); + ptr += modulePathLength; + + WriteDebugFieldToPacket( &ptr, imageEntry.imageDebugInfo ); + + *outptr = queueBuffer; + *outsize = bufferSize; +} +void Profiler::SendImageInfo( const ImageEntry& imageEntry ) +{ + void* serializePtr = nullptr; + size_t size = 0; + SerializeImageEntry( imageEntry, &serializePtr, &size ); + + // First sending the Data + SendSingleDataPacket( serializePtr, size); + tracy_free( serializePtr ); + + // Then sending that he receveived a image Update + QueueItem item; + tracy::MemWrite( &item.hdr.type, (int)QueueType::ImageUpdate); + NeedDataSize( QueueDataSize[(int)QueueType::ImageUpdate] ); + + AppendData( &item, QueueDataSize[(int)QueueType::ImageUpdate] ); + +} + void Profiler::HandleSymbolQueueItem( const SymbolQueueItem& si ) { switch( si.type ) { case SymbolQueueItemType::CallstackFrame: { - DecodeCallStackPtrStatus unusedStatus; - const auto frameData = DecodeCallstackPtr( si.ptr, &unusedStatus ); + DecodeCallStackPtrStatus mask = DecodeCallStackPtrStatusFlags::SymbolMissing; + const auto frameData = DecodeCallstackPtr( si.ptr, &mask ); + + + if ( mask & DecodeCallStackPtrStatusFlags::NewModuleFound ) + { + + std::lock_guard mutexguard{ GetModuleCacheMutexForRead() }; + + const ImageEntry* entry = GetImageEntryFromPtr(si.ptr); + assert( entry == nullptr ); + + void* imageData = nullptr; + size_t dataSize = 0; + SerializeImageEntry( *entry, &imageData, &dataSize ); + + TracyLfqPrepare( QueueType::ImageUpdate ); + tracy::MemWrite( &item->imageEntry.payload, (uint64_t)imageData ); + tracy::MemWrite( &item->imageEntry.payloadSize, (uint64_t)dataSize ); + TracyLfqCommit; + } + + auto data = tracy_malloc_fast( sizeof( CallstackEntry ) * frameData.size ); memcpy( data, frameData.data, sizeof( CallstackEntry ) * frameData.size ); TracyLfqPrepare( QueueType::CallstackFrameSize ); @@ -4088,107 +4198,23 @@ void Profiler::SendCachedModulesInformation() { for ( auto& it : *kernelDrivers ) { - SendModuleInfo( it ); + SendImageInfo( it ); } } const FastVector* moduleCache = GetUserImageInfos(); - if(moduleCache) + if( moduleCache ) { for ( auto& it : *moduleCache ) { - SendModuleInfo( it ); + SendImageInfo( it ); } } #endif } -#ifdef TRACY_HAS_CALLSTACK -static void WriteDebugFieldToPacket( uint8_t** ptr, int* currentPacketSize, const ImageDebugInfo& imageDebugInfo ) -{ - MemWrite( *ptr, imageDebugInfo.debugFormat ); - *ptr += sizeof( imageDebugInfo.debugFormat ); - *currentPacketSize += sizeof( imageDebugInfo.debugFormat ); - - const uint32_t dataSize = static_cast( imageDebugInfo.debugDataSize ); - - MemWrite( *ptr, dataSize ); - *ptr += sizeof( dataSize ); - *currentPacketSize += sizeof( dataSize ); - - memcpy( *ptr, imageDebugInfo.debugData, dataSize ); - *ptr += dataSize; - *currentPacketSize += dataSize; -} - -void Profiler::SendModuleInfo( const ImageEntry& imageEntry ) -{ - static constexpr int EndOfString = 1; - - const uint32_t moduleNameLength = ( imageEntry.name ? strlen( imageEntry.name ) : 0 ) + EndOfString; - const uint32_t modulePathLength = ( imageEntry.path ? strlen( imageEntry.path ) : 0 ) + EndOfString; - - QueueItem item; - tracy::MemWrite( &item.hdr.type, (int)QueueType::DataPacket ); - const size_t baseModuleInfo = sizeof( imageEntry.start ) + sizeof( imageEntry.end ) + - sizeof( moduleNameLength ) + moduleNameLength + - sizeof( modulePathLength ) + modulePathLength + - sizeof( imageEntry.imageDebugInfo.debugFormat ); - - const uint32_t debugFormatSize = 0 + sizeof( uint32_t ) // DebugDataSize - + static_cast( imageEntry.imageDebugInfo.debugDataSize ); - const size_t bufferSize = baseModuleInfo + debugFormatSize; - void* queueBuffer = tracy_malloc( bufferSize ); - - int size = 0; - - uint8_t* ptr = (uint8_t*)queueBuffer; - - // baseModuleInfo - MemWrite( ptr, imageEntry.start ); - ptr += sizeof( imageEntry.start ); - size += sizeof( imageEntry.start ); - - MemWrite( ptr, imageEntry.end ); - ptr += sizeof( imageEntry.end ); - size += sizeof( imageEntry.end ); - - MemWrite( ptr, moduleNameLength ); - ptr += sizeof( moduleNameLength ); - size += sizeof( moduleNameLength ); - - memcpy( ptr, imageEntry.name ? imageEntry.name : "", moduleNameLength ); - ptr += moduleNameLength; - size += moduleNameLength; - - MemWrite( ptr, modulePathLength ); - ptr += sizeof( modulePathLength ); - size += sizeof( modulePathLength ); - - memcpy( ptr, imageEntry.path ? imageEntry.path : "", modulePathLength ); - ptr += modulePathLength; - size += modulePathLength; - - WriteDebugFieldToPacket( &ptr, &size, imageEntry.imageDebugInfo ); - - - assert( size == bufferSize ); - - SendSingleDataPacket( queueBuffer, bufferSize, PacketDataType::ImageEntry ); - - QueueItem item2; - tracy::MemWrite( &item2.hdr.type, (int)QueueType::ModuleUpdate ); - - static_assert( tracy::QueueDataSize[(int)QueueType::ModuleUpdate] == sizeof( QueueItem ), "Size mismatch" ); - - NeedDataSize( sizeof( item2 ) ); - AppendData( &item2, sizeof( item2 ) ); - - tracy_free( queueBuffer ); -} -#endif void Profiler::SendCallstack( int32_t depth, const char* skipBefore ) { diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index ebc7564615..d28de30d89 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -777,7 +777,7 @@ class Profiler void SendSecondString( const char* ptr ) { SendSecondString( ptr, strlen( ptr ) ); } void SendSecondString( const char* ptr, size_t len ); - void SendSingleDataPacket( void* ptr, size_t totalSize, PacketDataType type ); + void SendSingleDataPacket( void* ptr, size_t totalSize ); // Allocated source location data layout: // 2b payload size @@ -922,7 +922,7 @@ class Profiler void SendCachedModulesInformation(); #ifdef TRACY_HAS_CALLSTACK - void SendModuleInfo( const ImageEntry& moduleCacheEntry ); + void SendImageInfo( const ImageEntry& moduleCacheEntry); #endif static tracy_force_inline void SendCallstackSerial( void* ptr ) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index e0e6d606b4..f5f2c9f1f1 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -420,6 +420,15 @@ static ImageCache* s_krnlCache = nullptr; static ImageCache* s_krnlSymbolsCache = nullptr; #endif +const ImageEntry* GetImageEntryFromPtr(uint64_t ptr) +{ + if(IsKernelAddress(ptr)) + { + return s_krnlCache->FindEntryFromAddr(ptr); + } + else return s_imageCache->FindEntryFromAddr(ptr); +} + void CreateImageCaches() { assert( s_imageCache == nullptr && s_krnlCache == nullptr ); @@ -881,14 +890,14 @@ void InitCallstack() // symbols during that time. const char* noInitLoadEnv = GetEnvVar("TRACY_NO_DBGHELP_INIT_LOAD"); const bool initTimeModuleLoad = !(noInitLoadEnv && noInitLoadEnv[0] == '1'); - if (!initTimeModuleLoad) + if ( !initTimeModuleLoad ) { TracyDebug("TRACY: skipping init time dbghelper module load\n"); } std::lock_guard mutexguard{ s_cacheMutex }; - if (initTimeModuleLoad) + if ( initTimeModuleLoad ) { CacheProcessDrivers(); CacheProcessModules(); @@ -903,7 +912,7 @@ void InitCallstack() void EndCallstack() { DestroyImageCaches(); - if (s_DbgHelpSymHandle) + if ( s_DbgHelpSymHandle ) { SymCleanup(s_DbgHelpSymHandle); s_DbgHelpSymHandle = 0; @@ -1103,6 +1112,8 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ bool moduleNotFound = false; ModuleNameAndBaseAddress moduleNameAndAddress = GetModuleNameAndPrepareSymbols( ptr, &moduleNotFound ); + + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatusFlags::Success; if( moduleNotFound ) { @@ -1116,12 +1127,15 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ // Set base address to ptr so that cb_data[0].symAddr=0 moduleNameAndAddress = { "[unknown]", ptr }; } - else moduleNotFound = false; // We managed to load the module information + else { + *_decodeCallStackPtrStatus |= DecodeCallStackPtrStatusFlags::NewModuleFound; + moduleNotFound = false; // We managed to load the module information + } } else { // We're on the server, it does not have a way to get information about the module - *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::ModuleMissing; + *_decodeCallStackPtrStatus |= DecodeCallStackPtrStatusFlags::ModuleMissing; } } @@ -1174,9 +1188,8 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ const auto symValid = SymFromAddr( proc, ptr, nullptr, si ) != 0; - if( symValid ) *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; - else { - *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::SymbolMissing; + if( !symValid ) { + *_decodeCallStackPtrStatus |= DecodeCallStackPtrStatusFlags::SymbolMissing; #ifdef TRACY_VERBOSE static bool doOnce = true; if(doOnce) diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index c84450ee2f..f3d99e20c5 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -44,15 +44,21 @@ namespace tracy static constexpr bool has_callstack() { return true; } -enum struct DecodeCallStackPtrStatus +enum DecodeCallStackPtrStatusFlags : uint8_t { - Success, - ModuleMissing, - SymbolMissing, + Success = 0, + ModuleMissing = 1 << 1, + SymbolMissing = 1 << 2, + ErrorMask = 0b11, + + NewModuleFound = 1 << 3, + Count }; +using DecodeCallStackPtrStatus = uint8_t; + enum struct ImageDebugFormatId : uint8_t { NoDebugFormat, @@ -105,6 +111,7 @@ struct ImageEntry void PreventSymbolResolution(); std::recursive_mutex& GetModuleCacheMutexForRead(); +const ImageEntry* GetImageEntryFromPtr( uint64_t ptr ); CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ); const char* DecodeCallstackPtrFast( uint64_t ptr ); diff --git a/public/common/TracyProtocol.hpp b/public/common/TracyProtocol.hpp index 0629f8c364..a65989d572 100644 --- a/public/common/TracyProtocol.hpp +++ b/public/common/TracyProtocol.hpp @@ -49,7 +49,6 @@ enum ServerQuery : uint8_t ServerQueryParameter, ServerQueryFiberName, ServerQueryExternalName, - ServerQueryModuleUpdate, // Items above are high priority. Split order must be preserved. See IsQueryPrio(). ServerQueryDisconnect, ServerQueryCallstackFrame, diff --git a/public/common/TracyQueue.hpp b/public/common/TracyQueue.hpp index b6dfe40946..c0f114e413 100644 --- a/public/common/TracyQueue.hpp +++ b/public/common/TracyQueue.hpp @@ -68,7 +68,7 @@ enum class QueueType : uint8_t SourceCodeMetadata, FiberEnter, FiberLeave, - ModuleUpdate, + ImageUpdate, DataPacket, Terminate, KeepAlive, @@ -700,20 +700,12 @@ static constexpr size_t MaxModule = 1024; struct QueueImageEntry { - + uint64_t payload; + uint64_t payloadSize; }; -enum struct PacketDataType : int -{ - EMPTY = 0, - ImageEntry, -}; - -static_assert( PacketDataType::EMPTY == (PacketDataType)0, "Empty must be First" ); - struct QueueDataPacket { - PacketDataType packetDataType; uint16_t packetSize; }; @@ -876,7 +868,7 @@ static constexpr size_t QueueDataSize[] = { sizeof( QueueHeader ) + sizeof( QueueFiberEnter ), sizeof( QueueHeader ) + sizeof( QueueFiberLeave ), // above items must be first - sizeof( QueueItem ), // image datas, + sizeof( QueueHeader ) + sizeof( QueueImageEntry ), // image datas, sizeof( QueueHeader ) + sizeof( QueueDataPacket ), // DataPacket sizeof( QueueHeader ), // terminate sizeof( QueueHeader ), // keep alive diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index d74d70841a..e45a9d969a 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -3186,7 +3186,7 @@ void Worker::DispatchFailure( const QueueItem& ev, const char*& ptr ) const QueueDataPacket* packet = reinterpret_cast( ptr ); ptr += sizeof( QueueHeader ); ptr += sizeof( QueueDataPacket ); - AddDataPacket( reinterpret_cast( ptr ), packet->packetSize, packet->packetDataType ); + AddDataPacket( reinterpret_cast( ptr ), packet->packetSize ); ptr += packet->packetSize; } break; @@ -3390,7 +3390,7 @@ bool Worker::DispatchProcess( const QueueItem& ev, const char*& ptr ) ptr += sizeof( QueueHeader ); const QueueDataPacket* packet = reinterpret_cast( ptr ); ptr += sizeof( QueueDataPacket ); - AddDataPacket( reinterpret_cast( ptr ), packet->packetSize, packet->packetDataType ); + AddDataPacket( reinterpret_cast( ptr ), packet->packetSize ); ptr += packet->packetSize; return true; } @@ -3896,11 +3896,8 @@ void Worker::AddSecondString( const char* str, size_t sz ) m_pendingSecondString = StoreString( str, sz ); } -void Worker::AddDataPacket( const void* data, size_t size, PacketDataType dataType ) +void Worker::AddDataPacket( const void* data, size_t size ) { - assert( m_pendingDataPacket.type == PacketDataType::EMPTY && m_pendingDataPacket.data == nullptr && dataType != PacketDataType::EMPTY ); - - m_pendingDataPacket.type = dataType; m_pendingDataPacket.data = static_cast( tracy_malloc( size ) ); memcpy( m_pendingDataPacket.data, data, size ); } @@ -4063,22 +4060,20 @@ uint64_t Worker::GetCanonicalPointer( const CallstackFrameId& id ) const void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySymbols ) { // If we already have the information - if (m_data.callstackFrameMap.count( frameId ) != 0) + if ( m_data.callstackFrameMap.count( frameId ) != 0 ) { return; } uint64_t symbolAddress = GetCanonicalPointer( frameId ); -#ifdef TRACY_SELF_PROFILE // Self profiling will use the old path and query symbols - DecodeCallStackPtrStatus status = DecodeCallStackPtrStatus::SymbolMissing; -#else - DecodeCallStackPtrStatus status = DecodeCallStackPtrStatus::Count; + DecodeCallStackPtrStatus status = DecodeCallStackPtrStatusFlags::SymbolMissing; +#ifndef TRACY_SELF_PROFILE // Self profiling will use the old path and query symbols if( m_symbolConfig.m_attemptResolutionByWorker ) { // TODO: offload to a worker thread CallstackEntryData outCallStack = DecodeCallstackPtr( symbolAddress, &status ); - if ( status == DecodeCallStackPtrStatus::Success ) + if( ( status & DecodeCallStackPtrStatusFlags::ErrorMask ) == DecodeCallStackPtrStatusFlags::Success ) { const uint32_t imageNameIdx = StoreString( outCallStack.imageName , strlen( outCallStack.imageName ) ).idx; assert(outCallStack.size <= 255); @@ -4105,33 +4100,21 @@ void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySy assert( m_pendingCallstackSubframes == 0 ); } } - else - { - status = DecodeCallStackPtrStatus::SymbolMissing; - } #endif if(querySymbols) { - switch (status) + if ( status & DecodeCallStackPtrStatusFlags::ModuleMissing ) { - break; - case DecodeCallStackPtrStatus::ModuleMissing: - // TODO: actually only query module info, and then try to resolve again locally. + // TODO: actually only query module info, and then try to resolve again locally. // Or we could just rely on the user triggering a new symbol resolution manually. m_pendingCallstackFrames++; Query( ServerQueryCallstackFrame, symbolAddress ); - break; - case DecodeCallStackPtrStatus::Count: - assert( false && "Missing error handling ?" ); - [[fallthrough]]; - case DecodeCallStackPtrStatus::SymbolMissing: + } + else if ( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) + { // Fallback to asking the client for the symbol since we couldn't find it. m_pendingCallstackFrames++; Query( ServerQueryCallstackFrame, symbolAddress ); - break; - case tracy::DecodeCallStackPtrStatus::Success: - default: - break; } } } @@ -4593,13 +4576,12 @@ uint32_t Worker::GetSecondStringIdx() return idx; } -void Worker::AccessPendingData( uint8_t** ptr, size_t* sz, PacketDataType* type ) +void Worker::AccessPendingData( uint8_t** ptr, size_t* sz) { - assert( m_pendingDataPacket.type != PacketDataType::EMPTY && m_pendingDataPacket.data != nullptr ); + assert( m_pendingDataPacket.data != nullptr ); *ptr = m_pendingDataPacket.data; *sz = m_pendingDataPacket.dataSize; - *type = m_pendingDataPacket.type; } void Worker::FreePendingData() @@ -4607,7 +4589,6 @@ void Worker::FreePendingData() tracy_free( m_pendingDataPacket.data ); m_pendingDataPacket.data = nullptr; m_pendingDataPacket.dataSize = 0; - m_pendingDataPacket.type = PacketDataType::EMPTY; } StringLocation Worker::StoreString( const char* str, size_t sz ) @@ -4929,10 +4910,12 @@ bool Worker::Process( const QueueItem& ev ) break; case QueueType::FiberLeave: ProcessFiberLeave( ev.fiberLeave ); - break; - case QueueType::ModuleUpdate: + break; + case QueueType::ImageUpdate: + { DispatchImageEntry(ev.imageEntry); - break; + break; + } default: assert( false ); break; @@ -5257,7 +5240,7 @@ void Worker::ResolveSymbolLocally() uint64_t symbolAddress = GetCanonicalPointer( it.first ); DecodeCallStackPtrStatus status; CallstackEntryData outCallStack = DecodeCallstackPtr( symbolAddress, &status ); - if ( status == DecodeCallStackPtrStatus::Success ) + if ( ( status & DecodeCallStackPtrStatusFlags::ErrorMask ) == DecodeCallStackPtrStatusFlags::Success ) { assert( outCallStack.size <= 255 ); assert( toResolveData.imageName.Idx() == StoreString( outCallStack.imageName, strlen(outCallStack.imageName)).idx); @@ -8936,78 +8919,65 @@ void Worker::CacheSourceFiles() void Worker::DispatchImageEntry( const QueueImageEntry& ev ) { - uint8_t* ptrToData = nullptr; - size_t s = 0; - PacketDataType dataType; - AccessPendingData( &ptrToData, &s, &dataType ); + uint8_t* ptrToData = nullptr; + size_t s = 0; + AccessPendingData(&ptrToData, &s); - switch( dataType ) - { - case PacketDataType::ImageEntry: - { - uint64_t baseAddress = MemRead( ptrToData ); - ptrToData += sizeof( baseAddress ); - uint64_t end = MemRead(ptrToData); - ptrToData += sizeof( end ); + uint64_t baseAddress = MemRead(ptrToData); + ptrToData += sizeof(baseAddress); - uint32_t moduleNameSize = MemRead( ptrToData ); - ptrToData += sizeof( moduleNameSize ); + uint64_t end = MemRead(ptrToData); + ptrToData += sizeof(end); - assert( (char)(ptrToData[moduleNameSize - 1]) == '\0' && "missing end of string" ); - char* moduleName = (char*)tracy_malloc_fast( moduleNameSize ); - memcpy( moduleName, ptrToData, moduleNameSize ); - ptrToData += moduleNameSize; - StringLocation moduleNameStringLocation = StoreString( moduleName, strlen( moduleName ) ); + uint32_t moduleNameSize = MemRead(ptrToData); + ptrToData += sizeof(moduleNameSize); - uint32_t modulePathSize = MemRead( ptrToData ); - ptrToData += sizeof(modulePathSize); + assert((char)(ptrToData[moduleNameSize - 1]) == '\0' && "missing end of string"); + char* moduleName = (char*)tracy_malloc_fast(moduleNameSize); + memcpy(moduleName, ptrToData, moduleNameSize); + ptrToData += moduleNameSize; - assert( (char)(ptrToData[modulePathSize - 1]) == '\0' && "missing end of string" ); - char* modulePath = (char*)tracy_malloc_fast( modulePathSize ); - memcpy( modulePath, ptrToData, modulePathSize ); - ptrToData += modulePathSize; - StringLocation modulePathStringLocation = StoreString( modulePath , strlen( modulePath )); + uint32_t modulePathSize = MemRead(ptrToData); + ptrToData += sizeof(modulePathSize); + assert((char)(ptrToData[modulePathSize - 1]) == '\0' && "missing end of string"); + char* modulePath = (char*)tracy_malloc_fast(modulePathSize); + memcpy(modulePath, ptrToData, modulePathSize); + ptrToData += modulePathSize; - ImageDebugFormatId debugFormat = MemRead( ptrToData ); - ptrToData += sizeof( ImageDebugFormatId ); - - ImageEntry moduleCacheEntry = - { - .start = baseAddress, - .end = end, - .name = moduleName, - .path = modulePath, - }; - moduleCacheEntry.imageDebugInfo.debugFormat = debugFormat; + ImageDebugFormatId debugFormat = MemRead(ptrToData); + ptrToData += sizeof(ImageDebugFormatId); - if ( debugFormat != ImageDebugFormatId::NoDebugFormat ) - { + ImageEntry moduleCacheEntry = + { + .start = baseAddress, + .end = end, + .name = moduleName, + .path = modulePath, + }; + moduleCacheEntry.imageDebugInfo.debugFormat = debugFormat; - uint32_t debugFormatSize = MemRead( ptrToData ); - ptrToData += sizeof( debugFormatSize ); + if (debugFormat != ImageDebugFormatId::NoDebugFormat) + { - uint8_t* debugData = (uint8_t*)tracy_malloc( debugFormatSize ); - memcpy( debugData, ptrToData, debugFormatSize ); - ptrToData += debugFormatSize; + uint32_t debugFormatSize = MemRead(ptrToData); + ptrToData += sizeof(debugFormatSize); - ImageDebugInfo& imageDebugInfo = moduleCacheEntry.imageDebugInfo; + uint8_t* debugData = (uint8_t*)tracy_malloc(debugFormatSize); + memcpy(debugData, ptrToData, debugFormatSize); + ptrToData += debugFormatSize; - imageDebugInfo.debugData = debugData; - imageDebugInfo.debugDataSize = debugFormatSize; + ImageDebugInfo& imageDebugInfo = moduleCacheEntry.imageDebugInfo; - } + imageDebugInfo.debugData = debugData; + imageDebugInfo.debugDataSize = debugFormatSize; - CacheImageAndLoadDebugInfo( moduleCacheEntry, m_symbolConfig.m_attemptResolutionByWorker ); + } - break; - } - default: - break; - } + CacheImageAndLoadDebugInfo(moduleCacheEntry, m_symbolConfig.m_attemptResolutionByWorker); - FreePendingData(); + FreePendingData(); } } diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index daa03b9572..5af80d4e8b 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -437,7 +437,6 @@ class Worker struct PendingDataPacket { - PacketDataType type; size_t dataSize; uint8_t* data; }; @@ -910,7 +909,7 @@ class Worker void AddSingleString( const char* str, size_t sz ); void AddSingleStringFailure( const char* str, size_t sz ); void AddSecondString( const char* str, size_t sz ); - void AddDataPacket( const void* data, size_t size , PacketDataType dataType ); + void AddDataPacket( const void* data, size_t size ); void AddExternalName( uint64_t ptr, const char* str, size_t sz ); void AddExternalThreadName( uint64_t ptr, const char* str, size_t sz ); void AddFrameImageData( const char* data, size_t sz ); @@ -939,7 +938,7 @@ class Worker uint32_t GetSingleStringIdx(); uint32_t GetSecondStringIdx(); - void AccessPendingData( uint8_t** ptr, size_t* sz, PacketDataType* type ); + void AccessPendingData( uint8_t** ptr, size_t* sz ); void FreePendingData(); const ContextSwitch* const GetContextSwitchDataImpl( uint64_t thread ); From b0fb501e2a95f70bc61cf3b1aa5e0274b5b0d059 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Thu, 20 Mar 2025 10:33:05 +0100 Subject: [PATCH 24/40] fix decodeCallStack flag for linux --- public/common/TracyCallstack.cpp | 15 ++++-- public/common/TracyCallstack.hpp | 84 +++++++++++++++++--------------- server/TracyWorker.cpp | 31 +++++++----- 3 files changed, 75 insertions(+), 55 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index f5f2c9f1f1..5fc0039a7d 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -1797,7 +1797,16 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ backtrace_syminfo( cb_bts, ptr, SymInfoCallback, SymInfoError, nullptr ); } - *_decodeCallStackPtrStatus = imageName ? DecodeCallStackPtrStatus::Success : DecodeCallStackPtrStatus::SymbolMissing; + if ( imageName ) + { + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatusFlags::Success; + + } + else + { + + *_decodeCallStackPtrStatus |= DecodeCallStackPtrStatusFlags::SymbolMissing; + } return { cb_data, uint8_t( cb_num ), imageName ? imageName : "[unknown]" }; } #ifdef __linux @@ -1812,7 +1821,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ cb_data[0].line = 0; cb_data[0].symLen = symbolEntry->end - symbolEntry->start; cb_data[0].symAddr = symbolEntry->start; - *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatusFlags::Success; return { cb_data, 1, symbolEntry->path ? symbolEntry->path : "" }; } } @@ -1823,7 +1832,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ cb_data[0].line = 0; cb_data[0].symLen = 0; cb_data[0].symAddr = 0; - *_decodeCallStackPtrStatus = DecodeCallStackPtrStatus::Success; + *_decodeCallStackPtrStatus = DecodeCallStackPtrStatusFlags::Success; return { cb_data, 1, "" }; } diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index f3d99e20c5..51683846ed 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -5,13 +5,56 @@ #include "../common/TracyForceInline.hpp" #include "TracyCallstack.h" +namespace tracy +{ + +enum DecodeCallStackPtrStatusFlags : uint8_t +{ + Success = 0, + ModuleMissing = 1 << 1, + SymbolMissing = 1 << 2, + + ErrorMask = 0b11, + + NewModuleFound = 1 << 3, + + Count +}; + +using DecodeCallStackPtrStatus = uint8_t; +enum struct ImageDebugFormatId : uint8_t +{ + NoDebugFormat, + PdbDebugFormat, + GNUDebugFormat, + ElfDebugFormat +}; + +struct ImageDebugInfo +{ + ImageDebugFormatId debugFormat; + uint8_t* debugData; + uint32_t debugDataSize; +}; + +struct ImageEntry +{ + uint64_t start; + uint64_t end; + char* name; + char* path; + + ImageDebugInfo imageDebugInfo; +}; +} + #ifndef TRACY_HAS_CALLSTACK namespace tracy { static constexpr bool has_callstack() { return false; } static tracy_force_inline void* Callstack( int32_t /*depth*/ ) { return nullptr; } -void PreventSymbolResolution() { } +inline void PreventSymbolResolution() { } } #else @@ -44,28 +87,6 @@ namespace tracy static constexpr bool has_callstack() { return true; } -enum DecodeCallStackPtrStatusFlags : uint8_t -{ - Success = 0, - ModuleMissing = 1 << 1, - SymbolMissing = 1 << 2, - - ErrorMask = 0b11, - - NewModuleFound = 1 << 3, - - Count -}; - -using DecodeCallStackPtrStatus = uint8_t; - -enum struct ImageDebugFormatId : uint8_t -{ - NoDebugFormat, - PdbDebugFormat, - GNUDebugFormat, - ElfDebugFormat -}; struct CallstackSymbolData { @@ -91,23 +112,6 @@ struct CallstackEntryData const char* imageName; }; -struct ImageDebugInfo -{ - ImageDebugFormatId debugFormat; - uint8_t* debugData; - uint32_t debugDataSize; -}; - -struct ImageEntry -{ - uint64_t start; - uint64_t end; - char* name; - char* path; - - ImageDebugInfo imageDebugInfo; -}; - void PreventSymbolResolution(); std::recursive_mutex& GetModuleCacheMutexForRead(); diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index e45a9d969a..e722752b38 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -26,9 +26,9 @@ #define ZDICT_STATIC_LINKING_ONLY #include -#include "../common/TracyCallstack.hpp" -#include "TracyDebugModulesHeaderFile.hpp" +#include "../public/common/TracyCallstack.hpp" +#include "../public/common/TracyFastVector.hpp" #include "../public/common/TracyProtocol.hpp" #include "../public/common/TracyStackFrames.hpp" #include "../public/common/TracySystem.hpp" @@ -44,6 +44,7 @@ #include "TracyTaskDispatch.hpp" #include "TracyWorker.hpp" #include "tracy_pdqsort.h" +#include "TracyAlloc.hpp" #ifdef TRACY_ENABLE // The worker should only have TRACY_ENABLE when self profiling #define TRACY_SELF_PROFILE @@ -287,7 +288,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb , m_traceVersion( CurrentVersion ) , m_loadTime( 0 ) { -#ifndef TRACY_SELF_PROFILE // Self profiling will use the old path and query symbols +#if defined(TRACY_HAS_CALLSTACK) && !defined( TRACY_SELF_PROFILE) // Self profiling will use the old path and query symbols InitCallstack(); #endif m_data.sourceLocationExpand.push_back( 0 ); @@ -331,7 +332,7 @@ Worker::Worker( const char* name, const char* program, const std::vector Date: Fri, 21 Mar 2025 09:46:39 +0100 Subject: [PATCH 25/40] Fix build (missing stdint.h include) --- public/common/TracyCallstack.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index 51683846ed..bf54d1d731 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -1,6 +1,8 @@ #ifndef __TRACYCALLSTACK_HPP__ #define __TRACYCALLSTACK_HPP__ +#include + #include "../common/TracyApi.h" #include "../common/TracyForceInline.hpp" #include "TracyCallstack.h" From c22db64d8f0c917bb458544131d905493be8bc72 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Tue, 22 Apr 2025 16:08:13 +0200 Subject: [PATCH 26/40] fix assert bad conditon --- public/client/TracyProfiler.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 6bd76e9022..c003f1428a 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -3546,7 +3546,7 @@ void Profiler::HandleSymbolQueueItem( const SymbolQueueItem& si ) std::lock_guard mutexguard{ GetModuleCacheMutexForRead() }; const ImageEntry* entry = GetImageEntryFromPtr(si.ptr); - assert( entry == nullptr ); + assert( entry != nullptr ); void* imageData = nullptr; size_t dataSize = 0; From 4135d4c2fa06b3eefc140c318364870d7c5971d9 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Tue, 22 Apr 2025 17:16:15 +0200 Subject: [PATCH 27/40] SymbolResolutionConfig construct from config --- profiler/src/profiler/TracyView.cpp | 4 ++-- profiler/src/profiler/TracyView.hpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/profiler/src/profiler/TracyView.cpp b/profiler/src/profiler/TracyView.cpp index fc04bcc194..a0bafccecb 100644 --- a/profiler/src/profiler/TracyView.cpp +++ b/profiler/src/profiler/TracyView.cpp @@ -36,7 +36,7 @@ namespace tracy double s_time = 0; View::View( void(*cbMainThread)(const std::function&, bool), const char* addr, uint16_t port, ImFont* fixedWidth, ImFont* smallFont, ImFont* bigFont, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, const Config& config, AchievementsMgr* amgr ) - : m_worker( addr, port, config.memoryLimit == 0 ? -1 : ( config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100 ), Worker::SymbolResolutionConfig{ config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient } ) + : m_worker( addr, port, config.memoryLimit == 0 ? -1 : ( config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100 ), SymbolResolutionConfigFromConfig(config)) , m_staticView( false ) , m_viewMode( ViewMode::LastFrames ) , m_viewModeHeuristicTry( true ) @@ -70,7 +70,7 @@ View::View( void(*cbMainThread)(const std::function&, bool), const char* } View::View( void(*cbMainThread)(const std::function&, bool), FileRead& f, ImFont* fixedWidth, ImFont* smallFont, ImFont* bigFont, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, const Config& config, AchievementsMgr* amgr ) - : m_worker( f, { config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient }, EventType::All, true, false ) + : m_worker( f,SymbolResolutionConfigFromConfig(config), EventType::All, true, false ) , m_filename( f.GetFilename() ) , m_staticView( true ) , m_viewMode( ViewMode::Paused ) diff --git a/profiler/src/profiler/TracyView.hpp b/profiler/src/profiler/TracyView.hpp index 6217fae8c9..9ff50bbb65 100644 --- a/profiler/src/profiler/TracyView.hpp +++ b/profiler/src/profiler/TracyView.hpp @@ -57,6 +57,7 @@ struct LockDraw; struct PlotDraw; struct FlameGraphContext; +static inline const Worker::SymbolResolutionConfig SymbolResolutionConfigFromConfig( const Config& config ) { return { config.symbolsAttemptResolutionByServer, config.symbolsPreventResolutionByClient }; } class View { @@ -143,6 +144,7 @@ class View const MessageData* GetMessageHighlight() const { return m_msgHighlight; } uint32_t GetLockInfoWindow() const { return m_lockInfoWindow; } + tracy_force_inline bool& Vis( const void* ptr ) { auto it = m_visMap.find( ptr ); From 11cd6373ec551d0a70ef55b7ffd62a9c7b968bf7 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Thu, 24 Apr 2025 15:52:26 +0200 Subject: [PATCH 28/40] Fix DecodeCallStackPtrStatusFlags values --- public/common/TracyCallstack.hpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index bf54d1d731..252e93eee9 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -13,13 +13,13 @@ namespace tracy enum DecodeCallStackPtrStatusFlags : uint8_t { Success = 0, - ModuleMissing = 1 << 1, - SymbolMissing = 1 << 2, + ModuleMissing = 1 << 0, + SymbolMissing = 1 << 1, ErrorMask = 0b11, - NewModuleFound = 1 << 3, - + NewModuleFound = 1 << 2, + Count }; From adeff0a86e2d6a837c3c19725f6eec4a22d1f46e Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Thu, 24 Apr 2025 15:52:48 +0200 Subject: [PATCH 29/40] Better memory layout for ImageDebugInfo --- public/common/TracyCallstack.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/public/common/TracyCallstack.hpp b/public/common/TracyCallstack.hpp index 252e93eee9..7f7601c54c 100644 --- a/public/common/TracyCallstack.hpp +++ b/public/common/TracyCallstack.hpp @@ -35,8 +35,8 @@ enum struct ImageDebugFormatId : uint8_t struct ImageDebugInfo { ImageDebugFormatId debugFormat; - uint8_t* debugData; uint32_t debugDataSize; + uint8_t* debugData; }; struct ImageEntry From 3a53e18f744c7c778dbe4b0149848b2e080b5242 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Thu, 24 Apr 2025 15:53:03 +0200 Subject: [PATCH 30/40] symbolsAttemptResolutionByServer set to false by default --- profiler/src/profiler/TracyConfig.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/profiler/src/profiler/TracyConfig.hpp b/profiler/src/profiler/TracyConfig.hpp index 1df4d132b6..52684742b2 100644 --- a/profiler/src/profiler/TracyConfig.hpp +++ b/profiler/src/profiler/TracyConfig.hpp @@ -20,7 +20,7 @@ struct Config int dynamicColors = 1; bool forceColors = false; int shortenName = (int)ShortenName::NoSpaceAndNormalize; - bool symbolsAttemptResolutionByServer = true; + bool symbolsAttemptResolutionByServer = false; bool symbolsPreventResolutionByClient = false; }; From ace2660ea7a88306659e6c846922ce4eeb069f0a Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Fri, 25 Apr 2025 11:17:35 +0200 Subject: [PATCH 31/40] add message window when symbol data missing in source view. --- profiler/src/profiler/TracyImGui.hpp | 16 ++++++++++++++++ profiler/src/profiler/TracySourceView.cpp | 10 +++++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/profiler/src/profiler/TracyImGui.hpp b/profiler/src/profiler/TracyImGui.hpp index 505d91d00c..7c35105b08 100644 --- a/profiler/src/profiler/TracyImGui.hpp +++ b/profiler/src/profiler/TracyImGui.hpp @@ -83,6 +83,22 @@ static constexpr const uint32_t AsmSyntaxColors[] = { ImGui::TextUnformatted( text ); } +[[maybe_unused]] static inline void TextCenteredWindow( const char* text ) +{ + const ImGuiStyle& imguiStyle = ImGui::GetStyle(); + + const ImVec2 size = ImGui::CalcTextSize(text) + ImVec2(imguiStyle.FramePadding.x * 2.0f, imguiStyle.FramePadding.y * 2.0f); + const ImVec2 avail = ImGui::GetContentRegionAvail(); + + const ImVec2 off = ImVec2( (avail.x - size.x ) * 0.5f, ( avail.y - size.y ) * 0.5f); + if( off.x > 0.0f ) + ImGui::SetCursorPosX( ImGui::GetCursorPosX() + off.x ); + if( off.y > 0.0f ) + ImGui::SetCursorPosY( ImGui::GetCursorPosY() + off.y ); + + ImGui::TextUnformatted( text ); +} + [[maybe_unused]] static inline bool ButtonCentered( const char* text ) { const auto tw = ImGui::CalcTextSize( text ).x + ImGui::GetStyle().FramePadding.x * 2; diff --git a/profiler/src/profiler/TracySourceView.cpp b/profiler/src/profiler/TracySourceView.cpp index f68697822a..ec43327114 100644 --- a/profiler/src/profiler/TracySourceView.cpp +++ b/profiler/src/profiler/TracySourceView.cpp @@ -1157,8 +1157,16 @@ void SourceView::RenderSymbolView( Worker& worker, View& view ) const auto shortenName = view.GetShortenName(); auto sym = worker.GetSymbolData( m_symAddr ); - if( sym == nullptr ) return; // Might no have received symbol info yet + if( sym == nullptr ) + { + ImGui::PushFont( m_bigFont ); + TextCenteredWindow( ICON_FA_PERSON_DIGGING "Waiting for symbol resolution" ); + ImGui::PopFont(); + return; + } + + ImGui::PushFont( m_bigFont ); ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) ); if( ButtonDisablable( " " ICON_FA_CARET_LEFT " ", m_historyCursor <= 1 ) ) From 22e62a3061bd1ff7e6fbc837c5ae0f27ce43c36d Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Fri, 25 Apr 2025 11:18:19 +0200 Subject: [PATCH 32/40] return unresolve symbol when failing symaddr --- public/common/TracyCallstack.cpp | 28 ++++++++++++++++++---------- server/TracyWorker.cpp | 14 +++++++++++--- 2 files changed, 29 insertions(+), 13 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 5fc0039a7d..571341b186 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -1102,6 +1102,18 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) 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, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus ) { #ifdef TRACY_DBGHELP_LOCK @@ -1117,6 +1129,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ if( moduleNotFound ) { + // only the client can have a processHandle as s_DbgHelpSymHandle if( s_DbgHelpSymHandle == GetCurrentProcess() ) { // We're on the client or self profiling, try to load a potentially new module. @@ -1144,15 +1157,8 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ #ifdef TRACY_DBGHELP_LOCK DBGHELP_UNLOCK; #endif - - cb_data[0].symAddr = ptr - moduleNameAndAddress.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, moduleNameAndAddress.name }; + + return MakeUnresolvedCallstackEntryData(ptr, moduleNameAndAddress); } int write; @@ -1188,8 +1194,9 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ const auto symValid = SymFromAddr( proc, ptr, nullptr, si ) != 0; - if( !symValid ) { + if( symValid == FALSE) { *_decodeCallStackPtrStatus |= DecodeCallStackPtrStatusFlags::SymbolMissing; + #ifdef TRACY_VERBOSE static bool doOnce = true; if(doOnce) @@ -1201,6 +1208,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ doOnce = false; } #endif + return MakeUnresolvedCallstackEntryData(ptr, moduleNameAndAddress); } diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index e722752b38..8694abcc48 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -4101,18 +4101,26 @@ void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySy assert( m_pendingCallstackSubframes == 0 ); } + + if( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) + { + tracy_free_fast( (void*)outCallStack.data[0].file ); + tracy_free_fast( (void*)outCallStack.data[0].name ); + } + } #endif - if(querySymbols) + if( querySymbols ) { - if ( status & DecodeCallStackPtrStatusFlags::ModuleMissing ) + + if( status & DecodeCallStackPtrStatusFlags::ModuleMissing ) { // TODO: actually only query module info, and then try to resolve again locally. // Or we could just rely on the user triggering a new symbol resolution manually. m_pendingCallstackFrames++; Query( ServerQueryCallstackFrame, symbolAddress ); } - else if ( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) + else if( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) { // Fallback to asking the client for the symbol since we couldn't find it. m_pendingCallstackFrames++; From c29c45df71ce473c6b5ec306bd247a2e601deb1e Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Tue, 15 Jul 2025 10:19:50 +0200 Subject: [PATCH 33/40] [Fix] Fix spelling mistake --- profiler/src/profiler/TracySourceView.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/profiler/src/profiler/TracySourceView.cpp b/profiler/src/profiler/TracySourceView.cpp index ec43327114..1e3e9c439d 100644 --- a/profiler/src/profiler/TracySourceView.cpp +++ b/profiler/src/profiler/TracySourceView.cpp @@ -1158,10 +1158,9 @@ void SourceView::RenderSymbolView( Worker& worker, View& view ) const auto shortenName = view.GetShortenName(); auto sym = worker.GetSymbolData( m_symAddr ); - if( sym == nullptr ) { ImGui::PushFont( m_bigFont ); - TextCenteredWindow( ICON_FA_PERSON_DIGGING "Waiting for symbol resolution" ); + TextCenteredWindow( ICON_FA_PERSON_DIGGING "Waiting for symbols resolution" ); ImGui::PopFont(); return; } From e0d0380dfc224348703a2a3c6b063815a0bb2c14 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Tue, 15 Jul 2025 16:50:13 +0200 Subject: [PATCH 34/40] Add first attempt for async profiler symbol resolution use of 2 SPSCQueue one for Woker->WorkerSymbolRes and another for WorkerSymbolRes->Worker, To Do use some fix amout of CallstackEntry array raither to give each CallstackEntryData an array of CallstackEntry (like frame in flight with vulkan). --- public/common/TracyCallstack.cpp | 47 ++++----- public/common/TracyCallstack.hpp | 5 +- server/TracyWorker.cpp | 165 +++++++++++++++++++++++-------- server/TracyWorker.hpp | 18 ++++ 4 files changed, 170 insertions(+), 65 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 571341b186..a00d5689d0 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -481,11 +481,7 @@ void FormatImageName(char** moduleCacheName, const char* imageName, uint32_t ima #if TRACY_HAS_CALLSTACK == 1 -enum { MaxCbTrace = 64 }; -enum { MaxNameSize = 8*1024 }; -int cb_num; -CallstackEntry cb_data[MaxCbTrace]; HANDLE s_DbgHelpSymHandle = 0; @@ -1104,14 +1100,15 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) static CallstackEntryData MakeUnresolvedCallstackEntryData( uint64_t ptr, ModuleNameAndBaseAddress moduleNameAndBaseAddress ) { - cb_data[0].symAddr = ptr - moduleNameAndBaseAddress.baseAddr; - cb_data[0].symLen = 0; + CallstackEntry out; + out.symAddr = ptr - moduleNameAndBaseAddress.baseAddr; + out.symLen = 0; - cb_data[0].name = CopyStringFast( "[unresolved]" ); - cb_data[0].file = CopyStringFast( "[unknown]" ); - cb_data[0].line = 0; + out.name = CopyStringFast( "[unresolved]" ); + out.file = CopyStringFast( "[unknown]" ); + out.line = 0; - return { cb_data, 1, moduleNameAndBaseAddress.name }; + return { {out}, 1, moduleNameAndBaseAddress.name }; } CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus ) @@ -1163,6 +1160,9 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ int write; const auto proc = s_DbgHelpSymHandle; + CallstackEntryData outCallstackEntryData; + int data_cb = 0; + #if !defined TRACY_NO_CALLSTACK_INLINES BOOL doInline = FALSE; @@ -1178,13 +1178,13 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ if( doInline ) { write = inlineNum; - cb_num = 1 + inlineNum; + data_cb = 1 + inlineNum; } else #endif { write = 0; - cb_num = 1; + data_cb = 1; } char buf[sizeof( SYMBOL_INFO ) + MaxNameSize]; @@ -1222,25 +1222,25 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ if( res == 0 || line.LineNumber >= 0xF00000 ) { filename = "[unknown]"; - cb_data[write].line = 0; + outCallstackEntryData.data[write].line = 0; } else { filename = line.FileName; - cb_data[write].line = line.LineNumber; + outCallstackEntryData.data[write].line = line.LineNumber; } - cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); - cb_data[write].file = CopyStringFast( filename ); + outCallstackEntryData.data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); + outCallstackEntryData.data[write].file = CopyStringFast( filename ); if( symValid ) { - cb_data[write].symLen = si->Size; - cb_data[write].symAddr = si->Address; + outCallstackEntryData.data[write].symLen = si->Size; + outCallstackEntryData.data[write].symAddr = si->Address; } else { - cb_data[write].symLen = 0; - cb_data[write].symAddr = 0; + outCallstackEntryData.data[write].symLen = 0; + outCallstackEntryData.data[write].symAddr = 0; } } @@ -1249,7 +1249,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ { for( DWORD i=0; i -#include "../public/common/TracyCallstack.hpp" #include "../public/common/TracyFastVector.hpp" #include "../public/common/TracyProtocol.hpp" #include "../public/common/TracyStackFrames.hpp" @@ -287,10 +286,13 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb , m_callstackFrameStaging( nullptr ) , m_traceVersion( CurrentVersion ) , m_loadTime( 0 ) + , m_symbolAddressWorkerQueue( 8 * 1024) + , m_symAddrCallstackEntryDataQueu(8 * 1024) + { -#if defined(TRACY_HAS_CALLSTACK) && !defined( TRACY_SELF_PROFILE) // Self profiling will use the old path and query symbols - InitCallstack(); -#endif + + m_symbolWorker = std::thread([this] { SetThreadName( "Tracy Server Symbol Worker"); RunSymbolWorker();}); + m_data.sourceLocationExpand.push_back( 0 ); m_data.localThreadCompress.InitZero(); m_data.callstackPayload.push_back( nullptr ); @@ -312,6 +314,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb m_thread = std::thread( [this] { SetThreadName( "Tracy Worker" ); Exec(); } ); m_threadNet = std::thread( [this] { SetThreadName( "Tracy Network" ); Network(); } ); + } Worker::Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames, const SymbolResolutionConfig& symbolResConfig ) @@ -331,10 +334,10 @@ Worker::Worker( const char* name, const char* program, const std::vector 0 && !m_serverQueryQueuePrio.empty() ) { const auto toSend = std::min( m_serverQuerySpaceLeft, m_serverQueryQueuePrio.size() ); @@ -4074,15 +4080,42 @@ void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySy if( m_symbolConfig.m_attemptResolutionByWorker ) { // TODO: offload to a worker thread - CallstackEntryData outCallStack = DecodeCallstackPtr( symbolAddress, &status ); - if( ( status & DecodeCallStackPtrStatusFlags::ErrorMask ) == DecodeCallStackPtrStatusFlags::Success ) + m_symbolAddressWorkerQueue.emplace(symbolAddress); + //auto entry = DecodeCallstackPtr( symbolAddress ); + //HandleCallStackEntryData(symbolAddress, entry); + } +#endif +#if 0 + if( querySymbols ) + { + + if( status & DecodeCallStackPtrStatusFlags::ModuleMissing ) + { + // TODO: actually only query module info, and then try to resolve again locally. + // Or we could just rely on the user triggering a new symbol resolution manually. + m_pendingCallstackFrames++; + Query( ServerQueryCallstackFrame, symbolAddress ); + } + else if( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) + { + // Fallback to asking the client for the symbol since we couldn't find it. + m_pendingCallstackFrames++; + Query( ServerQueryCallstackFrame, symbolAddress ); + } + } +#endif +} + +void Worker::HandleCallStackEntryData( uint64_t symaddr, const CallstackEntryData& entry, DecodeCallStackPtrStatus decodeCallStackPtrStatus ,bool querySymbols ) +{ + if( ( decodeCallStackPtrStatus & DecodeCallStackPtrStatusFlags::ErrorMask ) == DecodeCallStackPtrStatusFlags::Success ) { - const uint32_t imageNameIdx = StoreString( outCallStack.imageName , strlen( outCallStack.imageName ) ).idx; - assert(outCallStack.size <= 255); + const uint32_t imageNameIdx = StoreString( entry.imageName , strlen( entry.imageName ) ).idx; + assert(entry.size <= 255); const QueueCallstackFrameSize queueCallstackFrameSize = { - .ptr = symbolAddress, - .size = outCallStack.size, + .ptr = symaddr, + .size = entry.size, }; m_pendingCallstackFrames++; @@ -4090,7 +4123,7 @@ void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySy for (size_t i = 0; i < queueCallstackFrameSize.size; i++) { - const auto& frame = outCallStack.data[i]; + const auto& frame = entry.data[i]; ProcessCallstackFrame( frame.line, frame.symAddr, frame.symLen, StoreString( frame.name, strlen( frame.name ) ).idx, StoreString( frame.file, strlen( frame.file ) ).idx, querySymbols ); @@ -4102,31 +4135,11 @@ void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySy assert( m_pendingCallstackSubframes == 0 ); } - if( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) + if( decodeCallStackPtrStatus & DecodeCallStackPtrStatusFlags::SymbolMissing ) { - tracy_free_fast( (void*)outCallStack.data[0].file ); - tracy_free_fast( (void*)outCallStack.data[0].name ); + tracy_free_fast( (void*)entry.data[0].file ); + tracy_free_fast( (void*)entry.data[0].name ); } - - } -#endif - if( querySymbols ) - { - - if( status & DecodeCallStackPtrStatusFlags::ModuleMissing ) - { - // TODO: actually only query module info, and then try to resolve again locally. - // Or we could just rely on the user triggering a new symbol resolution manually. - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, symbolAddress ); - } - else if( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) - { - // Fallback to asking the client for the symbol since we couldn't find it. - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, symbolAddress ); - } - } } void Worker::AddCallstackPayload( const char* _data, size_t _sz ) @@ -5247,7 +5260,7 @@ void Worker::ResolveSymbolLocally() CallstackFrameData& toResolveData = *it.second; if(toResolveData.data[0].name.Idx() == unresolvedStrIdx.Idx()) { - uint64_t symbolAddress = GetCanonicalPointer( it.first ); + const uint64_t symbolAddress = GetCanonicalPointer( it.first ); DecodeCallStackPtrStatus status; CallstackEntryData outCallStack = DecodeCallstackPtr( symbolAddress, &status ); if ( ( status & DecodeCallStackPtrStatusFlags::ErrorMask ) == DecodeCallStackPtrStatusFlags::Success ) @@ -8995,4 +9008,74 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) FreePendingData(); } +void Worker::RunSymbolWorker() +{ +#if defined( TRACY_HAS_CALLSTACK ) && !defined( TRACY_SELF_PROFILE ) // Self profiling will use the old path and query symbols + InitCallstack(); +#endif + + while ( !m_shutdown ) + { + auto si = m_symbolAddressWorkerQueue.front(); + + if ( si ) + { + DecodeCallStackPtrStatus status; + CallstackEntryData callstackEntryData = DecodeCallstackPtr( *si, &status ); + m_symAddrCallstackEntryDataQueu.emplace( *si, callstackEntryData, status ); + m_symbolAddressWorkerQueue.pop(); + } + else + { + std::this_thread::sleep_for( std::chrono::milliseconds(20) ); + } + + } +} + + void Worker::HandleSymbolWorkerJob() + { + // we are the worker thread in Exec so we have acquire the mutex before + + if (m_symAddrCallstackEntryDataQueu.empty()) + return; + + //constexpr size_t MaxSymbolPerTick = 10; + //size_t count = 0; + + // or proceed less than max number of symbol per tick + while ( !m_symAddrCallstackEntryDataQueu.empty() ) + { + auto entData = m_symAddrCallstackEntryDataQueu.front(); + + if (entData) + { + HandleCallStackEntryData( entData->symadd, entData->callstackEntryData, entData->decodeCallStackPtrStatus, true ); + if ( entData->decodeCallStackPtrStatus & DecodeCallStackPtrStatusFlags::ModuleMissing ) + { + // TODO: actually only query module info, and then try to resolve again locally. + // Or we could just rely on the user triggering a new symbol resolution manually. + m_pendingCallstackFrames++; + Query(ServerQueryCallstackFrame, entData->symadd); + } + else if ( entData->decodeCallStackPtrStatus & DecodeCallStackPtrStatusFlags::SymbolMissing ) + { + // Fallback to asking the client for the symbol since we couldn't find it. + m_pendingCallstackFrames++; + Query(ServerQueryCallstackFrame,entData->symadd ); + } + m_symAddrCallstackEntryDataQueu.pop(); + // count++; + } + else + { + // m_symAddrCallstackEntryDataQueu.front() return nullptr but not empty ?? + break; + } + + + } + + } + } diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 5af80d4e8b..c97d40790e 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -18,6 +18,8 @@ #include "../public/common/TracyProtocol.hpp" #include "../public/common/TracySocket.hpp" #include "../public/common/TracyDebugModulesHeaderFile.hpp" +#include "../public/common/TracyCallstack.hpp" +#include "../public/client/tracy_SPSCQueue.h" #include "tracy_robin_hood.h" #include "TracyEvent.hpp" @@ -916,7 +918,11 @@ class Worker void AddSymbolCode( uint64_t ptr, const char* data, size_t sz ); void AddSourceCode( uint32_t id, const char* data, size_t sz ); + void RunSymbolWorker(); + void HandleSymbolWorkerJob(); void TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySymbols = true ); + void HandleCallStackEntryData( uint64_t symaddr, const CallstackEntryData& entry, DecodeCallStackPtrStatus decodeCallStackPtrStatus, bool querySymbol = true); + tracy_force_inline void AddCallstackPayload( const char* data, size_t sz ); tracy_force_inline void AddCallstackAllocPayload( const char* data ); uint32_t MergeCallstacks( uint32_t first, uint32_t second ); @@ -1003,6 +1009,7 @@ class Worker std::thread m_thread; std::thread m_threadNet; + std::thread m_symbolWorker; std::atomic m_connected { false }; std::atomic m_hasData; std::atomic m_shutdown { false }; @@ -1135,6 +1142,17 @@ class Worker Vector m_inlineStack; std::vector m_pendingThreadHints; + + SPSCQueue m_symbolAddressWorkerQueue; + + struct ResolvedCallStack + { + uint64_t symadd; + CallstackEntryData callstackEntryData; + DecodeCallStackPtrStatus decodeCallStackPtrStatus; + }; + SPSCQueue m_symAddrCallstackEntryDataQueu; + }; } From 2b3c00dfa38810b52e8d377a5325a12c6353643c Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Wed, 16 Jul 2025 09:21:52 +0200 Subject: [PATCH 35/40] Revert "Add first attempt for async profiler symbol resolution" This reverts commit e0d0380dfc224348703a2a3c6b063815a0bb2c14. --- public/common/TracyCallstack.cpp | 47 +++++---- public/common/TracyCallstack.hpp | 5 +- server/TracyWorker.cpp | 165 ++++++++----------------------- server/TracyWorker.hpp | 18 ---- 4 files changed, 65 insertions(+), 170 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index a00d5689d0..571341b186 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -481,7 +481,11 @@ void FormatImageName(char** moduleCacheName, const char* imageName, uint32_t ima #if TRACY_HAS_CALLSTACK == 1 +enum { MaxCbTrace = 64 }; +enum { MaxNameSize = 8*1024 }; +int cb_num; +CallstackEntry cb_data[MaxCbTrace]; HANDLE s_DbgHelpSymHandle = 0; @@ -1100,15 +1104,14 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) static CallstackEntryData MakeUnresolvedCallstackEntryData( uint64_t ptr, ModuleNameAndBaseAddress moduleNameAndBaseAddress ) { - CallstackEntry out; - out.symAddr = ptr - moduleNameAndBaseAddress.baseAddr; - out.symLen = 0; + cb_data[0].symAddr = ptr - moduleNameAndBaseAddress.baseAddr; + cb_data[0].symLen = 0; - out.name = CopyStringFast( "[unresolved]" ); - out.file = CopyStringFast( "[unknown]" ); - out.line = 0; + cb_data[0].name = CopyStringFast( "[unresolved]" ); + cb_data[0].file = CopyStringFast( "[unknown]" ); + cb_data[0].line = 0; - return { {out}, 1, moduleNameAndBaseAddress.name }; + return { cb_data, 1, moduleNameAndBaseAddress.name }; } CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _decodeCallStackPtrStatus ) @@ -1160,9 +1163,6 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ int write; const auto proc = s_DbgHelpSymHandle; - CallstackEntryData outCallstackEntryData; - int data_cb = 0; - #if !defined TRACY_NO_CALLSTACK_INLINES BOOL doInline = FALSE; @@ -1178,13 +1178,13 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ if( doInline ) { write = inlineNum; - data_cb = 1 + inlineNum; + cb_num = 1 + inlineNum; } else #endif { write = 0; - data_cb = 1; + cb_num = 1; } char buf[sizeof( SYMBOL_INFO ) + MaxNameSize]; @@ -1222,25 +1222,25 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ if( res == 0 || line.LineNumber >= 0xF00000 ) { filename = "[unknown]"; - outCallstackEntryData.data[write].line = 0; + cb_data[write].line = 0; } else { filename = line.FileName; - outCallstackEntryData.data[write].line = line.LineNumber; + cb_data[write].line = line.LineNumber; } - outCallstackEntryData.data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); - outCallstackEntryData.data[write].file = CopyStringFast( filename ); + cb_data[write].name = symValid ? CopyStringFast( si->Name, si->NameLen ) : CopyStringFast( moduleNameAndAddress.name ); + cb_data[write].file = CopyStringFast( filename ); if( symValid ) { - outCallstackEntryData.data[write].symLen = si->Size; - outCallstackEntryData.data[write].symAddr = si->Address; + cb_data[write].symLen = si->Size; + cb_data[write].symAddr = si->Address; } else { - outCallstackEntryData.data[write].symLen = 0; - outCallstackEntryData.data[write].symAddr = 0; + cb_data[write].symLen = 0; + cb_data[write].symAddr = 0; } } @@ -1249,7 +1249,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ { for( DWORD i=0; i +#include "../public/common/TracyCallstack.hpp" #include "../public/common/TracyFastVector.hpp" #include "../public/common/TracyProtocol.hpp" #include "../public/common/TracyStackFrames.hpp" @@ -286,13 +287,10 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb , m_callstackFrameStaging( nullptr ) , m_traceVersion( CurrentVersion ) , m_loadTime( 0 ) - , m_symbolAddressWorkerQueue( 8 * 1024) - , m_symAddrCallstackEntryDataQueu(8 * 1024) - { - - m_symbolWorker = std::thread([this] { SetThreadName( "Tracy Server Symbol Worker"); RunSymbolWorker();}); - +#if defined(TRACY_HAS_CALLSTACK) && !defined( TRACY_SELF_PROFILE) // Self profiling will use the old path and query symbols + InitCallstack(); +#endif m_data.sourceLocationExpand.push_back( 0 ); m_data.localThreadCompress.InitZero(); m_data.callstackPayload.push_back( nullptr ); @@ -314,7 +312,6 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, const Symb m_thread = std::thread( [this] { SetThreadName( "Tracy Worker" ); Exec(); } ); m_threadNet = std::thread( [this] { SetThreadName( "Tracy Network" ); Network(); } ); - } Worker::Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames, const SymbolResolutionConfig& symbolResConfig ) @@ -334,10 +331,10 @@ Worker::Worker( const char* name, const char* program, const std::vector 0 && !m_serverQueryQueuePrio.empty() ) { const auto toSend = std::min( m_serverQuerySpaceLeft, m_serverQueryQueuePrio.size() ); @@ -4080,42 +4074,15 @@ void Worker::TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySy if( m_symbolConfig.m_attemptResolutionByWorker ) { // TODO: offload to a worker thread - m_symbolAddressWorkerQueue.emplace(symbolAddress); - //auto entry = DecodeCallstackPtr( symbolAddress ); - //HandleCallStackEntryData(symbolAddress, entry); - } -#endif -#if 0 - if( querySymbols ) - { - - if( status & DecodeCallStackPtrStatusFlags::ModuleMissing ) - { - // TODO: actually only query module info, and then try to resolve again locally. - // Or we could just rely on the user triggering a new symbol resolution manually. - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, symbolAddress ); - } - else if( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) - { - // Fallback to asking the client for the symbol since we couldn't find it. - m_pendingCallstackFrames++; - Query( ServerQueryCallstackFrame, symbolAddress ); - } - } -#endif -} - -void Worker::HandleCallStackEntryData( uint64_t symaddr, const CallstackEntryData& entry, DecodeCallStackPtrStatus decodeCallStackPtrStatus ,bool querySymbols ) -{ - if( ( decodeCallStackPtrStatus & DecodeCallStackPtrStatusFlags::ErrorMask ) == DecodeCallStackPtrStatusFlags::Success ) + CallstackEntryData outCallStack = DecodeCallstackPtr( symbolAddress, &status ); + if( ( status & DecodeCallStackPtrStatusFlags::ErrorMask ) == DecodeCallStackPtrStatusFlags::Success ) { - const uint32_t imageNameIdx = StoreString( entry.imageName , strlen( entry.imageName ) ).idx; - assert(entry.size <= 255); + const uint32_t imageNameIdx = StoreString( outCallStack.imageName , strlen( outCallStack.imageName ) ).idx; + assert(outCallStack.size <= 255); const QueueCallstackFrameSize queueCallstackFrameSize = { - .ptr = symaddr, - .size = entry.size, + .ptr = symbolAddress, + .size = outCallStack.size, }; m_pendingCallstackFrames++; @@ -4123,7 +4090,7 @@ void Worker::HandleCallStackEntryData( uint64_t symaddr, const CallstackEntryDat for (size_t i = 0; i < queueCallstackFrameSize.size; i++) { - const auto& frame = entry.data[i]; + const auto& frame = outCallStack.data[i]; ProcessCallstackFrame( frame.line, frame.symAddr, frame.symLen, StoreString( frame.name, strlen( frame.name ) ).idx, StoreString( frame.file, strlen( frame.file ) ).idx, querySymbols ); @@ -4135,11 +4102,31 @@ void Worker::HandleCallStackEntryData( uint64_t symaddr, const CallstackEntryDat assert( m_pendingCallstackSubframes == 0 ); } - if( decodeCallStackPtrStatus & DecodeCallStackPtrStatusFlags::SymbolMissing ) + if( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) { - tracy_free_fast( (void*)entry.data[0].file ); - tracy_free_fast( (void*)entry.data[0].name ); + tracy_free_fast( (void*)outCallStack.data[0].file ); + tracy_free_fast( (void*)outCallStack.data[0].name ); } + + } +#endif + if( querySymbols ) + { + + if( status & DecodeCallStackPtrStatusFlags::ModuleMissing ) + { + // TODO: actually only query module info, and then try to resolve again locally. + // Or we could just rely on the user triggering a new symbol resolution manually. + m_pendingCallstackFrames++; + Query( ServerQueryCallstackFrame, symbolAddress ); + } + else if( status & DecodeCallStackPtrStatusFlags::SymbolMissing ) + { + // Fallback to asking the client for the symbol since we couldn't find it. + m_pendingCallstackFrames++; + Query( ServerQueryCallstackFrame, symbolAddress ); + } + } } void Worker::AddCallstackPayload( const char* _data, size_t _sz ) @@ -5260,7 +5247,7 @@ void Worker::ResolveSymbolLocally() CallstackFrameData& toResolveData = *it.second; if(toResolveData.data[0].name.Idx() == unresolvedStrIdx.Idx()) { - const uint64_t symbolAddress = GetCanonicalPointer( it.first ); + uint64_t symbolAddress = GetCanonicalPointer( it.first ); DecodeCallStackPtrStatus status; CallstackEntryData outCallStack = DecodeCallstackPtr( symbolAddress, &status ); if ( ( status & DecodeCallStackPtrStatusFlags::ErrorMask ) == DecodeCallStackPtrStatusFlags::Success ) @@ -9008,74 +8995,4 @@ void Worker::DispatchImageEntry( const QueueImageEntry& ev ) FreePendingData(); } -void Worker::RunSymbolWorker() -{ -#if defined( TRACY_HAS_CALLSTACK ) && !defined( TRACY_SELF_PROFILE ) // Self profiling will use the old path and query symbols - InitCallstack(); -#endif - - while ( !m_shutdown ) - { - auto si = m_symbolAddressWorkerQueue.front(); - - if ( si ) - { - DecodeCallStackPtrStatus status; - CallstackEntryData callstackEntryData = DecodeCallstackPtr( *si, &status ); - m_symAddrCallstackEntryDataQueu.emplace( *si, callstackEntryData, status ); - m_symbolAddressWorkerQueue.pop(); - } - else - { - std::this_thread::sleep_for( std::chrono::milliseconds(20) ); - } - - } -} - - void Worker::HandleSymbolWorkerJob() - { - // we are the worker thread in Exec so we have acquire the mutex before - - if (m_symAddrCallstackEntryDataQueu.empty()) - return; - - //constexpr size_t MaxSymbolPerTick = 10; - //size_t count = 0; - - // or proceed less than max number of symbol per tick - while ( !m_symAddrCallstackEntryDataQueu.empty() ) - { - auto entData = m_symAddrCallstackEntryDataQueu.front(); - - if (entData) - { - HandleCallStackEntryData( entData->symadd, entData->callstackEntryData, entData->decodeCallStackPtrStatus, true ); - if ( entData->decodeCallStackPtrStatus & DecodeCallStackPtrStatusFlags::ModuleMissing ) - { - // TODO: actually only query module info, and then try to resolve again locally. - // Or we could just rely on the user triggering a new symbol resolution manually. - m_pendingCallstackFrames++; - Query(ServerQueryCallstackFrame, entData->symadd); - } - else if ( entData->decodeCallStackPtrStatus & DecodeCallStackPtrStatusFlags::SymbolMissing ) - { - // Fallback to asking the client for the symbol since we couldn't find it. - m_pendingCallstackFrames++; - Query(ServerQueryCallstackFrame,entData->symadd ); - } - m_symAddrCallstackEntryDataQueu.pop(); - // count++; - } - else - { - // m_symAddrCallstackEntryDataQueu.front() return nullptr but not empty ?? - break; - } - - - } - - } - } diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index c97d40790e..5af80d4e8b 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -18,8 +18,6 @@ #include "../public/common/TracyProtocol.hpp" #include "../public/common/TracySocket.hpp" #include "../public/common/TracyDebugModulesHeaderFile.hpp" -#include "../public/common/TracyCallstack.hpp" -#include "../public/client/tracy_SPSCQueue.h" #include "tracy_robin_hood.h" #include "TracyEvent.hpp" @@ -918,11 +916,7 @@ class Worker void AddSymbolCode( uint64_t ptr, const char* data, size_t sz ); void AddSourceCode( uint32_t id, const char* data, size_t sz ); - void RunSymbolWorker(); - void HandleSymbolWorkerJob(); void TryResolveCallStackIfNeeded( CallstackFrameId frameId, bool querySymbols = true ); - void HandleCallStackEntryData( uint64_t symaddr, const CallstackEntryData& entry, DecodeCallStackPtrStatus decodeCallStackPtrStatus, bool querySymbol = true); - tracy_force_inline void AddCallstackPayload( const char* data, size_t sz ); tracy_force_inline void AddCallstackAllocPayload( const char* data ); uint32_t MergeCallstacks( uint32_t first, uint32_t second ); @@ -1009,7 +1003,6 @@ class Worker std::thread m_thread; std::thread m_threadNet; - std::thread m_symbolWorker; std::atomic m_connected { false }; std::atomic m_hasData; std::atomic m_shutdown { false }; @@ -1142,17 +1135,6 @@ class Worker Vector m_inlineStack; std::vector m_pendingThreadHints; - - SPSCQueue m_symbolAddressWorkerQueue; - - struct ResolvedCallStack - { - uint64_t symadd; - CallstackEntryData callstackEntryData; - DecodeCallStackPtrStatus decodeCallStackPtrStatus; - }; - SPSCQueue m_symAddrCallstackEntryDataQueu; - }; } From 25a3d4f7034094ed8567874d58bcbaa508542b0e Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Tue, 22 Jul 2025 09:20:12 +0200 Subject: [PATCH 36/40] fix formating --- public/client/TracyProfiler.cpp | 36 +++++++++---------- public/common/TracyCallstack.cpp | 59 +++++++++++++++++--------------- 2 files changed, 47 insertions(+), 48 deletions(-) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 0e73b2b747..4fc4d32a0f 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -3301,7 +3301,7 @@ void Profiler::SendLongString( uint64_t str, const char* ptr, size_t len, QueueT void Profiler::SendSingleDataPacket( void* ptr, size_t totalSize ) { - assert(totalSize <= std::numeric_limits::max()); + assert( totalSize <= std::numeric_limits::max() ); static_assert(sizeof(QueueHeader) + sizeof(QueueDataPacket) == QueueDataSize[(int)QueueType::DataPacket], "Size mismatch"); @@ -3309,13 +3309,13 @@ void Profiler::SendSingleDataPacket( void* ptr, size_t totalSize ) NeedDataSize(QueueDataSize[(int)QueueType::DataPacket] + totalSize); QueueItem item; - tracy::MemWrite(&item.hdr.type, (int)QueueType::DataPacket); + tracy::MemWrite( &item.hdr.type, (int)QueueType::DataPacket ); uint16_t dataSize = uint16_t(totalSize); - tracy::MemWrite(&item.packet.packetSize, dataSize); + tracy::MemWrite( &item.packet.packetSize, dataSize ); - AppendDataUnsafe(&item, QueueDataSize[(int)QueueType::DataPacket]); - AppendDataUnsafe(ptr, dataSize); + AppendDataUnsafe( &item, QueueDataSize[(int)QueueType::DataPacket] ); + AppendDataUnsafe( ptr, dataSize ); } @@ -3506,14 +3506,13 @@ static void SerializeImageEntry( const ImageEntry& imageEntry, void** outptr, si const uint32_t moduleNameLength = ( imageEntry.name ? strlen( imageEntry.name ) : 0 ) + EndOfString; const uint32_t modulePathLength = ( imageEntry.path ? strlen( imageEntry.path ) : 0 ) + EndOfString; - const size_t baseModuleInfo = sizeof( imageEntry.start ) + sizeof( imageEntry.end ) + sizeof( moduleNameLength ) + moduleNameLength + sizeof( modulePathLength ) + modulePathLength + sizeof( imageEntry.imageDebugInfo.debugFormat ); - const uint32_t debugFormatSize = 0 + sizeof( uint32_t ) - + static_cast( imageEntry.imageDebugInfo.debugDataSize ); + const uint32_t debugFormatSize = sizeof( uint32_t ) + + imageEntry.imageDebugInfo.debugDataSize; const size_t bufferSize = baseModuleInfo + debugFormatSize; void* queueBuffer = tracy_malloc( bufferSize ); @@ -3548,17 +3547,17 @@ void Profiler::SendImageInfo( const ImageEntry& imageEntry ) void* serializePtr = nullptr; size_t size = 0; SerializeImageEntry( imageEntry, &serializePtr, &size ); - - // First sending the Data - SendSingleDataPacket( serializePtr, size); - tracy_free( serializePtr ); - // Then sending that he receveived a image Update - QueueItem item; - tracy::MemWrite( &item.hdr.type, (int)QueueType::ImageUpdate); - NeedDataSize( QueueDataSize[(int)QueueType::ImageUpdate] ); + // First send the Data + SendSingleDataPacket( serializePtr, size ); + tracy_free( serializePtr ); + + // Then send that it received an image Update which will use the previous Data + QueueItem item; + tracy::MemWrite( &item.hdr.type, (int)QueueType::ImageUpdate ); + NeedDataSize( QueueDataSize[(int)QueueType::ImageUpdate] ); - AppendData( &item, QueueDataSize[(int)QueueType::ImageUpdate] ); + AppendData( &item, QueueDataSize[(int)QueueType::ImageUpdate] ); } @@ -4208,9 +4207,6 @@ void Profiler::SendCachedModulesInformation() #endif } - - - void Profiler::SendCallstack( int32_t depth, const char* skipBefore ) { #ifdef TRACY_HAS_CALLSTACK diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 5f7d535a9f..eed451e11e 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -134,13 +134,16 @@ namespace tracy else return defaultValue; } - // 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) + // When "TRACY_SYMBOL_OFFLINE_RESOLVE" is set, symbols are not fully resolved at runtime. + // Instead, only the offset and image name are recorded, which is sufficient for offline resolution. + // If the server flag ServerFlags::PreventSymbolResolution is true, symbol resolution by the application is prevented, + // and resolution is handled exclusively by the server. #ifdef TRACY_SYMBOL_OFFLINE_RESOLVE static bool s_shouldResolveSymbolsOffline = true; #else static bool s_shouldResolveSymbolsOffline = false; #endif // TRACY_SYMBOL_OFFLINE_RESOLVE + void PreventSymbolResolution() { s_shouldResolveSymbolsOffline = true; } inline bool IsKernelAddress( uint64_t addr ) { @@ -176,7 +179,7 @@ namespace tracy } ~ImageCache() { Clear(); } - ImageEntry* CacheModuleWithDebugInfo( const ImageEntry& entry ) + ImageEntry* AddEntry( const ImageEntry& entry ) { m_sorted &= m_modCache.empty() ? true : (entry.start < m_modCache.back().start); ImageEntry* newEntry = m_modCache.push_next(); @@ -349,7 +352,7 @@ class ImageCacheLibbacktrace : public ImageCache } - cache->CacheModuleWithDebugInfo(image); + cache->AddEntry(image); cache->m_updated = true; return 0; @@ -466,17 +469,20 @@ const FastVector* GetKernelImageInfos() return &s_krnlCache->GetModuleData(); } -void FormatImageName(char** moduleCacheName, const char* imageName, uint32_t imageNameLength) +char* FormatImageName(const char* imageName, uint32_t imageNameLength) { - auto ptr = imageName + imageNameLength; - while (ptr > imageName && *ptr != '\\' && *ptr != '/') ptr--; - if (ptr > imageName) ptr++; + const char* ptr = imageName + imageNameLength; + while( ptr > imageName && *ptr != '\\' && *ptr != '/' ) ptr--; + if( ptr > imageName ) ptr++; const auto namelen = imageName + imageNameLength - ptr; - *moduleCacheName = (char*)tracy_malloc_fast(namelen + 3); - (*moduleCacheName)[0] = '['; - memcpy(*moduleCacheName + 1, ptr, namelen); - (*moduleCacheName)[namelen + 1] = ']'; - (*moduleCacheName)[namelen + 2] = '\0'; + + char* alloc = (char*)tracy_malloc_fast( namelen + 3 ); + alloc[0] = '['; + memcpy( alloc + 1, ptr, namelen ); + alloc[namelen + 1] = ']'; + alloc[namelen + 2] = '\0'; + + return alloc; } #if TRACY_HAS_CALLSTACK == 1 @@ -523,6 +529,7 @@ static constexpr DWORD CV_SIGNATURE_RSDS = 'SDSR'; // 'SDSR' bool GetModuleInfoFromPEHeaders( uint64_t baseOfDll, ImageDebugFormatId* debugFormat, uint8_t** debugInformationData, uint32_t* debugInformationSize ) { + // always true for loaded executables static constexpr bool MappedAsImage = true; PVOID BaseAddress = (void*)baseOfDll; @@ -541,14 +548,14 @@ bool GetModuleInfoFromPEHeaders( uint64_t baseOfDll, ImageDebugFormatId* debugFo } IMAGE_DEBUG_DIRECTORY* debugDirectory = static_cast( debugSectionData ); - for( size_t i=0; (i * sizeof( IMAGE_DEBUG_DIRECTORY ) ) < debugDirectoryCount; i++ ) + for( size_t i = 0; ( i * sizeof( IMAGE_DEBUG_DIRECTORY ) ) < debugDirectoryCount; i++ ) { const IMAGE_DEBUG_DIRECTORY& curDebugDirectory = debugDirectory[i]; if( debugDirectory[i].Type != IMAGE_DEBUG_TYPE_CODEVIEW ) continue; - CV_INFO_PDB70* pData = (CV_INFO_PDB70*)(uintptr_t( BaseAddress ) + + CV_INFO_PDB70* pData = (CV_INFO_PDB70*)( uintptr_t( BaseAddress ) + ( MappedAsImage ? debugDirectory[i].AddressOfRawData : debugDirectory[i].PointerToRawData) @@ -558,7 +565,7 @@ bool GetModuleInfoFromPEHeaders( uint64_t baseOfDll, ImageDebugFormatId* debugFo *debugFormat = ImageDebugFormatId::PdbDebugFormat; - const uint32_t pdbFileLength = strlen( (const char*)pData->PdbFileName ); + const size_t pdbFileLength = strlen( (const char*)pData->PdbFileName ); const uint32_t debugFormatSize = sizeof( PEImageDebugData ) + pdbFileLength + 1; *debugInformationData = (uint8_t*)tracy_malloc( debugFormatSize ); *debugInformationSize = debugFormatSize; @@ -622,8 +629,6 @@ void GetModuleInfoFromDbgHelp( const char* imageName, ImageEntry* moduleEntry ) static_assert( sizeof( pdbInfo->Signature ) == sizeof( moduleInfo.PdbSig70 ), "GUID size must match" ); memcpy( &pdbInfo->Signature, &moduleInfo.PdbSig70, sizeof( moduleInfo.PdbSig70 ) ); - const GUID* guidd = reinterpret_cast( &pdbInfo->Signature ); - char* pdbFileName = (char*)debugInfo.debugData + sizeof( PEImageDebugData ); memcpy( pdbFileName, moduleInfo.CVData, pdbFileNameLen + 1 ); } @@ -635,7 +640,7 @@ ImageEntry* CacheModuleInfo( const char* imageName, uint32_t imageNameLength, ui moduleEntry.start = baseOfDll; moduleEntry.end = baseOfDll + dllSize; moduleEntry.path = CopyStringFast( imageName, imageNameLength ); - FormatImageName( &moduleEntry.name, imageName, imageNameLength ); + moduleEntry.name = FormatImageName( imageName, imageNameLength ); ImageDebugFormatId debugFormat = ImageDebugFormatId::NoDebugFormat; uint8_t* debugData = nullptr; @@ -651,7 +656,7 @@ ImageEntry* CacheModuleInfo( const char* imageName, uint32_t imageNameLength, ui } std::lock_guard mutexguard{ s_cacheMutex }; - return s_imageCache->CacheModuleWithDebugInfo( moduleEntry ); + return s_imageCache->AddEntry( moduleEntry ); } ImageEntry* LoadSymbolsForModuleAndCache( const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) @@ -758,7 +763,6 @@ void DbgHelpInit( HANDLE symHandle, bool invadeProcess ) SymSetOptions( SYMOPT_LOAD_LINES - | SYMOPT_UNDNAME // TODO: check if tracy doesn't rely on this to find decorated names in the dissassembler | SYMOPT_DEFERRED_LOADS #ifndef NDEBUG | SYMOPT_DEBUG @@ -833,7 +837,7 @@ static void CacheProcessDrivers() } } - s_krnlCache->CacheModuleWithDebugInfo( kernelDriver ); + s_krnlCache->AddEntry( kernelDriver ); assert( kernelDriver.end == 0 && "kernel end should be zero" ); cnt++; } @@ -1037,11 +1041,11 @@ void CacheImageAndLoadDebugInfo( ImageEntry& imageEntry, bool loadDebugInfo ) { if ( IsKernelAddress( imageEntry.start ) ) { - s_krnlCache->CacheModuleWithDebugInfo( imageEntry ); + s_krnlCache->AddEntry( imageEntry ); } else { - s_imageCache->CacheModuleWithDebugInfo( imageEntry ); + s_imageCache->AddEntry( imageEntry ); } bool hasSymbolInfo = false; @@ -1092,7 +1096,7 @@ CallstackSymbolData DecodeSymbolAddress( uint64_t ptr ) DBGHELP_LOCK; #endif const auto res = SymGetLineFromAddr64( s_DbgHelpSymHandle, ptr, &displacement, &line ); - if ( res == 0 || line.LineNumber >= 0xF00000 ) + if( res == 0 || line.LineNumber >= 0xF00000 ) { sym.file = "[unknown]"; sym.line = 0; @@ -1292,8 +1296,7 @@ CallstackEntryData DecodeCallstackPtr( uint64_t ptr, DecodeCallStackPtrStatus* _ DBGHELP_UNLOCK; #endif - - return { cb_data, uint8_t(cb_num), moduleNameAndAddress.name }; + return { cb_data, uint8_t( cb_num ), moduleNameAndAddress.name }; } #elif defined(TRACY_USE_LIBBACKTRACE) @@ -1412,7 +1415,7 @@ static void InitKernelSymbols() kernelSymbol.name = strname; kernelSymbol.path = strmod; - s_krnlSymbolsCache->CacheModuleWithDebugInfo( kernelSymbol ); + s_krnlSymbolsCache->AddEntry( kernelSymbol ); } tracy_free_fast( linebuf ); From 937471311f7583b73953833c1bf4e61377cc3154 Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Tue, 22 Jul 2025 15:48:43 +0200 Subject: [PATCH 37/40] fix image deserialisation from file --- server/TracyWorker.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 8e7d2c0e69..a3b5ab34b4 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -1682,16 +1682,14 @@ Worker::Worker( FileRead& f, const SymbolResolutionConfig& symbolResConfig, Even auto DeserializeDebugField = [&]( ImageDebugInfo* debugField ) { - ImageDebugFormatId debugFormat; - f.Read( debugFormat ); + f.Read( debugField->debugFormat ); - if( debugFormat == ImageDebugFormatId::NoDebugFormat ) + if( debugField->debugFormat == ImageDebugFormatId::NoDebugFormat ) { debugField->debugDataSize = 0; debugField->debugData = nullptr; return; } - f.Read( debugField->debugDataSize ); debugField->debugData = (uint8_t*)tracy_malloc( debugField->debugDataSize ); f.Read( debugField->debugData, debugField->debugDataSize ); From 2b6bb9fb14d2ac966a5684ecdd912a4ef4567254 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Wed, 23 Jul 2025 13:36:57 +0200 Subject: [PATCH 38/40] Fix kernel symbols resolution on windows (image size is required even when we have Pdb files) --- public/common/TracyCallstack.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index eed451e11e..398f21ccac 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -607,6 +607,13 @@ void GetModuleInfoFromDbgHelp( const char* imageName, ImageEntry* moduleEntry ) } } + // We now know the size of the module, which is needed for proper offline symbol resolution + // Otherwise DbgHelp may assume the module is of size 4096 (1page), leading to unresolved symbols. + if( moduleEntry->end == 0 && moduleInfo.ImageSize ) + { + moduleEntry->end = moduleEntry->start + moduleInfo.ImageSize; + } + if( moduleInfo.CVSig != CV_SIGNATURE_RSDS ) // Do we have a pdb ? return; From b8f37d9209232730e16673212d2ebd005e2ddfef Mon Sep 17 00:00:00 2001 From: Gabriel Bon Date: Wed, 23 Jul 2025 13:56:38 +0200 Subject: [PATCH 39/40] Fix comment and naming --- public/common/TracyCallstack.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index 398f21ccac..a5dfb04ec4 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -641,13 +641,13 @@ void GetModuleInfoFromDbgHelp( const char* imageName, ImageEntry* moduleEntry ) } } -ImageEntry* CacheModuleInfo( const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) +ImageEntry* CacheModuleInfo( const char* imagePath, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) { ImageEntry moduleEntry = {}; moduleEntry.start = baseOfDll; moduleEntry.end = baseOfDll + dllSize; - moduleEntry.path = CopyStringFast( imageName, imageNameLength ); - moduleEntry.name = FormatImageName( imageName, imageNameLength ); + moduleEntry.path = CopyStringFast( imagePath, imageNameLength ); + moduleEntry.name = FormatImageName( imagePath, imageNameLength ); ImageDebugFormatId debugFormat = ImageDebugFormatId::NoDebugFormat; uint8_t* debugData = nullptr; @@ -666,12 +666,12 @@ ImageEntry* CacheModuleInfo( const char* imageName, uint32_t imageNameLength, ui return s_imageCache->AddEntry( moduleEntry ); } -ImageEntry* LoadSymbolsForModuleAndCache( const char* imageName, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) +ImageEntry* LoadSymbolsForModuleAndCache( const char* imagePath, uint32_t imageNameLength, uint64_t baseOfDll, uint32_t dllSize ) { - assert( s_DbgHelpSymHandle == GetCurrentProcess() ); // Only resolve we path if resolving current process - SymLoadModuleEx( s_DbgHelpSymHandle, nullptr, imageName, nullptr, baseOfDll, dllSize, nullptr, 0 ); + assert( s_DbgHelpSymHandle == GetCurrentProcess() ); // Only resolve with path if s_DbgHelpSymHandle is current process + SymLoadModuleEx( s_DbgHelpSymHandle, nullptr, imagePath, nullptr, baseOfDll, dllSize, nullptr, 0 ); - return CacheModuleInfo( imageName, imageNameLength, baseOfDll, dllSize ); + return CacheModuleInfo( imagePath, imageNameLength, baseOfDll, dllSize ); } struct ModuleNameAndBaseAddress From 6a707bb3e0b429245657efabb7fbd5e4f3a2aaa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Thu, 24 Jul 2025 11:10:00 +0200 Subject: [PATCH 40/40] Remove assert for kernelDriver.end since we know retrieve the module size to be able to resolve symbols offline --- public/common/TracyCallstack.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/public/common/TracyCallstack.cpp b/public/common/TracyCallstack.cpp index a5dfb04ec4..ab4bfae6f3 100644 --- a/public/common/TracyCallstack.cpp +++ b/public/common/TracyCallstack.cpp @@ -812,7 +812,7 @@ static void CacheProcessDrivers() ImageEntry kernelDriver{}; kernelDriver.start = (uint64_t)dev[i]; - kernelDriver.end = 0; + kernelDriver.end = 0; // Should be filled by GetModuleInfoFromDbgHelp kernelDriver.name = buf; kernelDriver.path = nullptr; kernelDriver.imageDebugInfo = {}; @@ -845,7 +845,6 @@ static void CacheProcessDrivers() } s_krnlCache->AddEntry( kernelDriver ); - assert( kernelDriver.end == 0 && "kernel end should be zero" ); cnt++; } }