From 5b920e1972c5d9de0efeaf477442376fe84d5c1e Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Sun, 21 Dec 2025 13:21:24 +0200 Subject: [PATCH 1/2] Say hello to TRACY_DEFAULT_MEMORY_PROFLER Enables default memory profiler on supported platforms --- CMakeLists.txt | 1 + cmake/server.cmake | 1 + public/TracyClient.cpp | 14 ++- public/client/TracyAlloc.cpp | 155 ++++++++++++++++++++++++++++++- public/client/TracyCallstack.cpp | 2 + public/client/TracyCallstack.hpp | 2 +- public/client/TracyProfiler.cpp | 6 +- public/client/TracyProfiler.hpp | 18 +++- public/client/TracySysPower.cpp | 1 + public/client/TracySysTime.cpp | 1 + public/client/TracySysTrace.cpp | 2 + public/client/tracy_rpmalloc.cpp | 10 +- public/common/TracyAlloc.hpp | 61 +++--------- public/common/tracy_lz4.cpp | 12 ++- 14 files changed, 220 insertions(+), 66 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 712e09753a..4a6bb3442a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -142,6 +142,7 @@ set_option(TRACY_SYMBOL_OFFLINE_RESOLVE "Instead of full runtime symbol resoluti set_option(TRACY_LIBBACKTRACE_ELF_DYNLOAD_SUPPORT "Enable libbacktrace to support dynamically loaded elfs in symbol resolution resolution after the first symbol resolve operation" OFF) set_option(TRACY_DEBUGINFOD "Enable debuginfod support" OFF) set_option(TRACY_IGNORE_MEMORY_FAULTS "Ignore instrumentation errors from memory free events that do not have a matching allocation" OFF) +set_option(TRACY_DEFAULT_MEMORY_PROFLER "Enable default memory profiler on supported platforms" OFF) # advanced set_option(TRACY_VERBOSE "[advanced] Verbose output from the profiler" OFF) diff --git a/cmake/server.cmake b/cmake/server.cmake index a76d1c1340..25cce47c86 100644 --- a/cmake/server.cmake +++ b/cmake/server.cmake @@ -1,6 +1,7 @@ set(TRACY_COMMON_DIR ${CMAKE_CURRENT_LIST_DIR}/../public/common) set(TRACY_COMMON_SOURCES + ../client/TracyAlloc.cpp tracy_lz4.cpp tracy_lz4hc.cpp TracySocket.cpp diff --git a/public/TracyClient.cpp b/public/TracyClient.cpp index 8e66975968..817ace6f6f 100644 --- a/public/TracyClient.cpp +++ b/public/TracyClient.cpp @@ -19,6 +19,7 @@ # pragma warning(push, 0) #endif +#include "client/TracyAlloc.cpp" #include "common/tracy_lz4.cpp" #include "client/TracyProfiler.cpp" #include "client/TracyCallstack.cpp" @@ -28,7 +29,6 @@ #include "common/TracySocket.cpp" #include "client/tracy_rpmalloc.cpp" #include "client/TracyDxt1.cpp" -#include "client/TracyAlloc.cpp" #include "client/TracyOverride.cpp" #include "client/TracyKCore.cpp" @@ -42,4 +42,16 @@ # pragma warning(pop) #endif +# ifdef TRACY_ENABLE_DEFAULT_MEMORY_PROFLER +class BlockMemoryProfileHooks +{ +public: + ~BlockMemoryProfileHooks() + { + ++tracy::DirectAlloc::s_direct; // at exit the application will try to free the static objects memory + // which will be traced by tracy::Profiler::MemFreeCallstack which needs those static objects + } +}; +static BlockMemoryProfileHooks __blockMemoryProfileHooks; +# endif #endif diff --git a/public/client/TracyAlloc.cpp b/public/client/TracyAlloc.cpp index c675b6d3f8..7ca384fbdd 100644 --- a/public/client/TracyAlloc.cpp +++ b/public/client/TracyAlloc.cpp @@ -1,15 +1,109 @@ #include "../common/TracyAlloc.hpp" +#if defined( TRACY_DEFAULT_MEMORY_PROFLER )/* && defined( TRACY_ON_DEMAND )*/ +# if __has_include( ) +# include "../client/TracyProfiler.hpp" +# include +# define TRACY_ENABLE_DEFAULT_MEMORY_PROFLER +# endif +#endif + +#ifndef TRACY_ENABLE_DEFAULT_MEMORY_PROFLER +namespace +{ +auto _malloc = &malloc; +auto _free = &free; +auto _calloc = &calloc; +auto _realloc = &realloc; +} // namespace { +#else +namespace +{ + +inline void* _malloc( size_t size ) +{ + static auto malloc_ = reinterpret_cast( dlsym( RTLD_NEXT, "malloc" ) ); + return malloc_( size ); +} +inline void _free( void* ptr ) +{ + static auto free_ = reinterpret_cast( dlsym( RTLD_NEXT, "free" ) ); + free_( ptr ); +} +inline void* _calloc( size_t nmemb, size_t size ) +{ + static auto calloc_ = reinterpret_cast( dlsym( RTLD_NEXT, "calloc" ) ); + return calloc_( nmemb, size ); +} + +inline void* _realloc( void* ptr, size_t size ) +{ + static auto realloc_ = reinterpret_cast( dlsym( RTLD_NEXT, "realloc" ) ); + return realloc_( ptr, size ); +} + +} // namespace { + +extern "C" +{ + void* malloc( size_t size ) + { + if( tracy::DirectAlloc::s_direct ) + return _malloc( size ); + tracy::DirectAlloc locker; + auto _ptr = _malloc( size ); + tracy::Profiler::MemAllocCallstack( _ptr, size, 50, false ); + return _ptr; + } + + void free( void* ptr ) + { + if( tracy::DirectAlloc::s_direct ) + return _free( ptr ); + tracy::DirectAlloc locker; + if( ptr ) + tracy::Profiler::MemFreeCallstack( ptr, 50, false ); + _free( ptr ); + } + + void* calloc( size_t nmemb, size_t size ) + { + if( tracy::DirectAlloc::s_direct ) + return _calloc( nmemb, size ); + tracy::DirectAlloc locker; + auto _ptr = _calloc( nmemb, size ); + tracy::Profiler::MemAllocCallstack( _ptr, nmemb * size, 50, false ); + return _ptr; + } + + void* realloc( void* ptr, size_t size ) + { + if( tracy::DirectAlloc::s_direct ) + return _realloc( ptr, size ); + tracy::DirectAlloc locker; + if( ptr ) + tracy::Profiler::MemFreeCallstack( ptr, 50, false ); + auto _ptr = _realloc( ptr, size ); + tracy::Profiler::MemAllocCallstack( _ptr, size, 50, false ); + return _ptr; + } +} // extern "C" { +#endif + #ifdef TRACY_USE_RPMALLOC -#include +# include -#include "../common/TracyForceInline.hpp" -#include "../common/TracyYield.hpp" +# include "../common/TracyForceInline.hpp" +# include "../common/TracyYield.hpp" +#endif namespace tracy { +thread_local int DirectAlloc::s_direct = 0; + +#ifdef TRACY_USE_RPMALLOC extern thread_local bool RpThreadInitDone; extern std::atomic RpInitDone; extern std::atomic RpInitLock; @@ -20,7 +114,11 @@ tracy_no_inline static void InitRpmallocPlumbing() if( !done ) { int expected = 0; - while( !RpInitLock.compare_exchange_weak( expected, 1, std::memory_order_release, std::memory_order_relaxed ) ) { expected = 0; YieldThread(); } + while( !RpInitLock.compare_exchange_weak( expected, 1, std::memory_order_release, std::memory_order_relaxed ) ) + { + expected = 0; + YieldThread(); + } const auto done = RpInitDone.load( std::memory_order_acquire ); if( !done ) { @@ -37,7 +135,54 @@ TRACY_API void InitRpmalloc() { if( !RpThreadInitDone ) InitRpmallocPlumbing(); } +#endif +TRACY_API void* tracy_malloc( size_t size ) +{ +# ifdef TRACY_USE_RPMALLOC + InitRpmalloc(); + return rpmalloc( size ); +# else + return _malloc( size ); +# endif } -#endif +TRACY_API void* tracy_malloc_fast( size_t size ) +{ +# ifdef TRACY_USE_RPMALLOC + return rpmalloc( size ); +# else + return _malloc( size ); +# endif +} + +TRACY_API void tracy_free( void* ptr ) +{ +# ifdef TRACY_USE_RPMALLOC + InitRpmalloc(); + rpfree( ptr ); +# else + _free( ptr ); +# endif +} + +TRACY_API void tracy_free_fast( void* ptr ) +{ +# ifdef TRACY_USE_RPMALLOC + rpfree( ptr ); +# else + _free( ptr ); +# endif +} + +TRACY_API void* tracy_realloc( void* ptr, size_t size ) +{ +# ifdef TRACY_USE_RPMALLOC + InitRpmalloc(); + return rprealloc( ptr, size ); +# else + return _realloc( ptr, size ); +# endif +} + +} // namespace tracy diff --git a/public/client/TracyCallstack.cpp b/public/client/TracyCallstack.cpp index 3cc41386b4..0082f21237 100644 --- a/public/client/TracyCallstack.cpp +++ b/public/client/TracyCallstack.cpp @@ -104,6 +104,7 @@ extern "C" const char* ___tracy_demangle( const char* mangled ) if( strlen( mangled ) > ___tracy_demangle_buffer_len ) return nullptr; int status; size_t len = ___tracy_demangle_buffer_len; + tracy::DirectAlloc lock; return abi::__cxa_demangle( mangled, ___tracy_demangle_buffer, &len, &status ); } #endif @@ -865,6 +866,7 @@ size_t s_kernelSymCnt; static void InitKernelSymbols() { + tracy::DirectAlloc lock; FILE* f = fopen( "/proc/kallsyms", "rb" ); if( !f ) return; tracy::FastVector tmpSym( 512 * 1024 ); diff --git a/public/client/TracyCallstack.hpp b/public/client/TracyCallstack.hpp index 7d8ed6e66c..04f2b41385 100644 --- a/public/client/TracyCallstack.hpp +++ b/public/client/TracyCallstack.hpp @@ -149,7 +149,7 @@ static tracy_force_inline void* Callstack( int32_t depth ) assert( depth >= 1 ); auto trace = (uintptr_t*)tracy_malloc( ( 1 + (size_t)depth ) * sizeof( uintptr_t ) ); - + tracy::DirectAlloc lock; #ifdef TRACY_LIBUNWIND_BACKTRACE size_t num = unw_backtrace( (void**)(trace+1), depth ); #else diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 2771f7a614..e72c6e914a 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -171,6 +171,7 @@ struct MappingInfo { // vector is sorted by ascending address too. static std::vector ParseMappings() { + tracy::DirectAlloc lock; std::vector result; FILE* file = fopen( "/proc/self/maps", "r" ); if( !file ) return result; @@ -513,6 +514,7 @@ static uint32_t GetHex( char*& ptr, int skip ) static const char* GetHostInfo() { + tracy::DirectAlloc lock; static char buf[1024]; auto ptr = buf; #if defined _WIN32 @@ -3889,7 +3891,8 @@ void Profiler::CalibrateDelay() void Profiler::ReportTopology() { -#ifndef TRACY_DELAYED_INIT + DirectAlloc lock; +# ifndef TRACY_DELAYED_INIT struct CpuData { uint32_t package; @@ -4183,6 +4186,7 @@ void Profiler::HandleSymbolCodeQuery( uint64_t symbol, uint32_t size ) void Profiler::HandleSourceCodeQuery( char* data, char* image, uint32_t id ) { + tracy::DirectAlloc lock; bool ok = false; FILE* f = fopen( data, "rb" ); if( f ) diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index 7f7646ecf9..892125ad82 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -842,16 +842,28 @@ class Profiler enum class DequeueStatus { DataDequeued, ConnectionLost, QueueEmpty }; enum class ThreadCtxStatus { Same, Changed, ConnectionLost }; - static void LaunchWorker( void* ptr ) { ((Profiler*)ptr)->Worker(); } + static void LaunchWorker( void* ptr ) + { + tracy::DirectAlloc lock; + ( (Profiler*)ptr )->Worker(); + } void Worker(); #ifndef TRACY_NO_FRAME_IMAGE - static void LaunchCompressWorker( void* ptr ) { ((Profiler*)ptr)->CompressWorker(); } + static void LaunchCompressWorker( void* ptr ) + { + tracy::DirectAlloc lock; + ( (Profiler*)ptr )->CompressWorker(); + } void CompressWorker(); #endif #ifdef TRACY_HAS_CALLSTACK - static void LaunchSymbolWorker( void* ptr ) { ((Profiler*)ptr)->SymbolWorker(); } + static void LaunchSymbolWorker( void* ptr ) + { + tracy::DirectAlloc lock; + ( (Profiler*)ptr )->SymbolWorker(); + } void SymbolWorker(); void HandleSymbolQueueItem( const SymbolQueueItem& si ); #endif diff --git a/public/client/TracySysPower.cpp b/public/client/TracySysPower.cpp index ed04b8b9d0..b13529342d 100644 --- a/public/client/TracySysPower.cpp +++ b/public/client/TracySysPower.cpp @@ -68,6 +68,7 @@ void SysPower::Tick() void SysPower::ScanDirectory( const char* path, int parent ) { + tracy::DirectAlloc lock; DIR* dir = opendir( path ); if( !dir ) return; struct dirent* ent; diff --git a/public/client/TracySysTime.cpp b/public/client/TracySysTime.cpp index cf7dd9b141..f9222155bb 100644 --- a/public/client/TracySysTime.cpp +++ b/public/client/TracySysTime.cpp @@ -55,6 +55,7 @@ void SysTime::ReadTimes() void SysTime::ReadTimes() { + tracy::DirectAlloc lock; uint64_t user, nice, system; FILE* f = fopen( "/proc/stat", "r" ); if( f ) diff --git a/public/client/TracySysTrace.cpp b/public/client/TracySysTrace.cpp index 55aa315200..74222ee399 100644 --- a/public/client/TracySysTrace.cpp +++ b/public/client/TracySysTrace.cpp @@ -990,6 +990,7 @@ static uint64_t* GetCallstackBlock( uint64_t cnt, RingBuffer& ring, uint64_t off void SysTraceWorker( void* ptr ) { + tracy::DirectAlloc lock; ThreadExitHandler threadExitHandler; SetThreadName( "Tracy Sampling" ); InitRpmalloc(); @@ -1397,6 +1398,7 @@ void SysTraceWorker( void* ptr ) void SysTraceGetExternalName( uint64_t thread, const char*& threadName, const char*& name ) { + tracy::DirectAlloc lock; FILE* f; char fn[256]; sprintf( fn, "/proc/%" PRIu64 "/comm", thread ); diff --git a/public/client/tracy_rpmalloc.cpp b/public/client/tracy_rpmalloc.cpp index c43b8cab9b..075883eeef 100644 --- a/public/client/tracy_rpmalloc.cpp +++ b/public/client/tracy_rpmalloc.cpp @@ -2705,11 +2705,13 @@ rpmalloc_initialize(void) { int rpmalloc_initialize_config(const rpmalloc_config_t* config) { - if (_rpmalloc_initialized) { - rpmalloc_thread_initialize(); + tracy::DirectAlloc lock; + if( _rpmalloc_initialized ) + { + rpmalloc_thread_initialize(); return 0; - } - _rpmalloc_initialized = 1; + } + _rpmalloc_initialized = 1; if (config) memcpy(&_memory_config, config, sizeof(rpmalloc_config_t)); diff --git a/public/common/TracyAlloc.hpp b/public/common/TracyAlloc.hpp index ddb0e5df65..394b7b35a4 100644 --- a/public/common/TracyAlloc.hpp +++ b/public/common/TracyAlloc.hpp @@ -3,8 +3,8 @@ #include +#include "TracyApi.h" #if defined TRACY_ENABLE && !defined __EMSCRIPTEN__ -# include "TracyApi.h" # include "TracyForceInline.hpp" # include "../client/tracy_rpmalloc.hpp" # define TRACY_USE_RPMALLOC @@ -13,59 +13,24 @@ namespace tracy { -#ifdef TRACY_USE_RPMALLOC -TRACY_API void InitRpmalloc(); -#else -static inline void InitRpmalloc() {} -#endif - -static inline void* tracy_malloc( size_t size ) +struct DirectAlloc { -#ifdef TRACY_USE_RPMALLOC - InitRpmalloc(); - return rpmalloc( size ); -#else - return malloc( size ); -#endif -} + inline DirectAlloc() { ++s_direct; } + inline ~DirectAlloc() { --s_direct; } + static thread_local int s_direct; +}; -static inline void* tracy_malloc_fast( size_t size ) -{ #ifdef TRACY_USE_RPMALLOC - return rpmalloc( size ); -#else - return malloc( size ); -#endif -} - -static inline void tracy_free( void* ptr ) -{ -#ifdef TRACY_USE_RPMALLOC - InitRpmalloc(); - rpfree( ptr ); -#else - free( ptr ); -#endif -} - -static inline void tracy_free_fast( void* ptr ) -{ -#ifdef TRACY_USE_RPMALLOC - rpfree( ptr ); +TRACY_API void InitRpmalloc(); #else - free( ptr ); +static inline void InitRpmalloc() {} #endif -} -static inline void* tracy_realloc( void* ptr, size_t size ) -{ -#ifdef TRACY_USE_RPMALLOC - InitRpmalloc(); - return rprealloc( ptr, size ); -#else - return realloc( ptr, size ); -#endif -} +TRACY_API void* tracy_malloc( size_t size ); +TRACY_API void* tracy_malloc_fast( size_t size ); +TRACY_API void tracy_free( void* ptr ); +TRACY_API void tracy_free_fast( void* ptr ); +TRACY_API void* tracy_realloc( void* ptr, size_t size ); } diff --git a/public/common/tracy_lz4.cpp b/public/common/tracy_lz4.cpp index 15d0990f82..e68eaeb58a 100644 --- a/public/common/tracy_lz4.cpp +++ b/public/common/tracy_lz4.cpp @@ -220,9 +220,15 @@ void LZ4_free(void* p); # define FREEMEM(p) LZ4_free(p) #else # include /* malloc, calloc, free */ -# define ALLOC(s) malloc(s) -# define ALLOC_AND_ZERO(s) calloc(1,s) -# define FREEMEM(p) free(p) +# ifdef TRACY_ENABLE_DEFAULT_MEMORY_PROFLER +# define ALLOC( s ) _malloc( s ) +# define ALLOC_AND_ZERO( s ) _calloc( 1, s ) +# define FREEMEM( p ) _free( p ) +# else +# define ALLOC( s ) malloc( s ) +# define ALLOC_AND_ZERO( s ) calloc( 1, s ) +# define FREEMEM( p ) free( p ) +# endif #endif #if ! LZ4_FREESTANDING From ada88df4c35cb900321f11ae0486682e77b10d0c Mon Sep 17 00:00:00 2001 From: BogDan Vatra Date: Fri, 9 Jan 2026 13:25:56 +0200 Subject: [PATCH 2/2] Optionally build also tracy's UI Profiler --- CMakeLists.txt | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a6bb3442a..fe3e7966f5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ endif() option(TRACY_STATIC "Whether to build Tracy as a static library" ${DEFAULT_STATIC}) option(TRACY_Fortran "Build Fortran bindings" OFF) option(TRACY_LTO "Enable Link-Time optimization" OFF) +option(TRACY_UI_PROFILER "Whether to build Tracy UI Profiler" OFF) if(TRACY_Fortran) enable_language(Fortran) @@ -284,3 +285,7 @@ if(TRACY_CLIENT_PYTHON) add_subdirectory(python) endif() + +if (TRACY_UI_PROFILER) + add_subdirectory(profiler) +endif()