diff --git a/CMakeLists.txt b/CMakeLists.txt index 58616393..2a5165bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -111,6 +111,7 @@ include(cmake/options.cmake) tracy_set_option(TRACY_ENABLE "Enable profiling" OFF TracyClient) tracy_set_option(TRACY_ON_DEMAND "On-demand profiling" OFF TracyClient) tracy_set_option_value(TRACY_CALLSTACK "Override the callstack collection depth for tracy zones" "" TracyClient) +tracy_set_option_value_as_string(TRACY_PLATFORM_HEADER "Path to a header providing TRACY_HAS_CUSTOM_* hooks for an unsupported platform" "" TracyClient) tracy_set_option(TRACY_NO_CALLSTACK "Disable all callstack related functionality" OFF TracyClient) tracy_set_option(TRACY_NO_CALLSTACK_INLINES "Disables the inline functions in callstacks" OFF TracyClient) tracy_set_option(TRACY_ONLY_LOCALHOST "Only listen on the localhost interface" OFF TracyClient) diff --git a/examples/CustomPlatform/CustomPlatform.cpp b/examples/CustomPlatform/CustomPlatform.cpp new file mode 100644 index 00000000..8ff0af71 --- /dev/null +++ b/examples/CustomPlatform/CustomPlatform.cpp @@ -0,0 +1,57 @@ +// Template implementations of the tracy::Platform* hooks. Pair with the +// platform header (see CustomPlatform.h) and link this into your final +// binary. + +#include +#include + +#include "CustomPlatform.h" + +namespace tracy +{ + +uint32_t PlatformGetThreadId() +{ + return 0; +} + +void PlatformGetHostname( char* buf, size_t size ) +{ + const char* placeholder = "(?)"; + if( size == 0 ) return; + const size_t n = strlen( placeholder ); + const size_t copy = n < size - 1 ? n : size - 1; + memcpy( buf, placeholder, copy ); + buf[copy] = '\0'; +} + +const char* PlatformGetUserLogin() +{ + return "(?)"; +} + +const char* PlatformGetUserFullName() +{ + return nullptr; +} + +bool PlatformSafeMemcpy( void* dst, const void* src, size_t size ) +{ + // Stub: report failure so Tracy skips the snapshot. Real impls use SEH + // on Win32, pipe(2) on POSIX, or an equivalent probe-and-copy primitive. + (void)dst; (void)src; (void)size; + return false; +} + +// Stubs forward to the C runtime. Swap in the allocator you actually want. + +void* PlatformMalloc( size_t size ) { return malloc( size ); } +void PlatformFree( void* ptr ) { free( ptr ); } +void* PlatformRealloc( void* ptr, size_t size ) { return realloc( ptr, size ); } + +void PlatformAllocatorInit() {} +void PlatformAllocatorThreadInit() {} +void PlatformAllocatorFinalize() {} +void PlatformAllocatorThreadFinalize(){} + +} diff --git a/examples/CustomPlatform/CustomPlatform.h b/examples/CustomPlatform/CustomPlatform.h new file mode 100644 index 00000000..a27d55f2 --- /dev/null +++ b/examples/CustomPlatform/CustomPlatform.h @@ -0,0 +1,73 @@ +// Template platform header for unsupported targets. +// +// Copy into your project, fill in the sections you need, and point Tracy at +// it via -DTRACY_PLATFORM_HEADER="\"my_platform.h\"". Provide the +// implementations in any TU linked into your final binary (see +// CustomPlatform.cpp). +// +// Use this only for the TRACY_HAS_CUSTOM_* hooks and matching Platform* +// declarations — don't set unrelated TRACY_* options here. Some are checked +// before this header is included, so the result would depend on which TU +// consulted them; set those at the build system level instead. +// +// For platform-specific features without a custom hook (call stacks, +// context switches, crash handling, system tracing, etc.), disable them at +// the build system level with the matching TRACY_NO_* macro. + +#ifndef __MY_TRACY_PLATFORM_H__ +#define __MY_TRACY_PLATFORM_H__ + +#include +#include + +namespace tracy +{ + +// --- Thread id -------------------------------------------------------------- +// +// Required if defaults in TracySystem.cpp do not matches your platform. +// Note pthread_self() is NOT suitable, it returns a library handle, not a kernel id. +//#define TRACY_HAS_CUSTOM_THREAD_ID +uint32_t PlatformGetThreadId(); + + +// --- User info -------------------------------------------------------------- +// +// Identifies the machine and user in the trace header. Return placeholder +// strings (e.g. "(?)") from any of these if your platform has no equivalent +// notion. +//#define TRACY_HAS_CUSTOM_USER_INFO +void PlatformGetHostname( char* buf, size_t size ); +const char* PlatformGetUserLogin(); +const char* PlatformGetUserFullName(); + + +// --- Safe memory copy ------------------------------------------------------- +// +// Tracy uses this to snapshot potentially-unmapped memory during sampling. +// Must not crash on unreadable input — return false instead. Plain memcpy() +// is NOT a valid implementation. +//#define TRACY_HAS_CUSTOM_SAFE_COPY +bool PlatformSafeMemcpy( void* dst, const void* src, size_t size ); + + +// --- Allocator -------------------------------------------------------------- +// +// Replaces Tracy's internal allocator. Drop in the system allocator, an +// in-house one, or any third-party allocator you like. Malloc/Free/Realloc +// must be thread-safe; ThreadInit is an optional prime, not a precondition. +// Finalize must also tear down the calling thread's per-thread state, the +// way rpmalloc_finalize() does — Tracy does not call ThreadFinalize for the +// shutdown thread before Finalize. +//#define TRACY_HAS_CUSTOM_ALLOCATOR +void* PlatformMalloc( size_t size ); +void PlatformFree( void* ptr ); +void* PlatformRealloc( void* ptr, size_t size ); +void PlatformAllocatorInit(); +void PlatformAllocatorThreadInit(); +void PlatformAllocatorFinalize(); +void PlatformAllocatorThreadFinalize(); + +} + +#endif diff --git a/manual/tracy.tex b/manual/tracy.tex index d32ecafe..46952301 100644 --- a/manual/tracy.tex +++ b/manual/tracy.tex @@ -669,6 +669,40 @@ Although the basic features will work without them, you'll have to grant elevate \item \texttt{-{}-pid=host} \end{itemize} +\subsubsection{Porting to unsupported platforms} +\label{customplatform} + +When Tracy is built for a platform that is not among the supported set, some of the platform-specific code paths it relies on may fail to compile or have undesired behavior. Rather than patching the \texttt{\#if} chains in Tracy itself, you can point Tracy at a \emph{platform header} that provides your own implementations of these primitives. + +Define \texttt{TRACY\_PLATFORM\_HEADER} at build time to the path of a header that Tracy will include from its internal translation units: + +\begin{lstlisting} +-DTRACY_PLATFORM_HEADER="\"my_platform.h\"" +\end{lstlisting} + +Inside that header, enable any subset of the hooks you need by defining the corresponding \texttt{TRACY\_HAS\_CUSTOM\_*} macro and declaring the matching \texttt{tracy::Platform*} function. Provide the implementations in a separate translation unit that is linked into your final binary. + +The available hooks are: + +\begin{itemize} +\item \texttt{TRACY\_HAS\_CUSTOM\_THREAD\_ID} $\rightarrow$ \texttt{tracy::PlatformGetThreadId()}. Required. +\item \texttt{TRACY\_HAS\_CUSTOM\_USER\_INFO} $\rightarrow$ \texttt{tracy::PlatformGetHostname()}, \texttt{tracy::PlatformGetUserLogin()}, \texttt{tracy::PlatformGetUserFullName()}. +\item \texttt{TRACY\_HAS\_CUSTOM\_SAFE\_COPY} $\rightarrow$ \texttt{tracy::PlatformSafeMemcpy()}. +\item \texttt{TRACY\_HAS\_CUSTOM\_ALLOCATOR} $\rightarrow$ \texttt{tracy::PlatformMalloc()}, \texttt{tracy::PlatformFree()}, \texttt{tracy::PlatformRealloc()}, \texttt{tracy::PlatformAllocatorInit()}, \texttt{tracy::PlatformAllocatorThreadInit()}, \texttt{tracy::PlatformAllocatorFinalize()}, \texttt{tracy::PlatformAllocatorThreadFinalize()}. +\end{itemize} + +Template files are provided in the repository ( \texttt{examples/CustomPlatform/CustomPlatform(.h|.cpp)} ). See \texttt{CustomPlatform.h} for the contract each \texttt{Platform*} function must satisfy (return values, threading guarantees, and footguns to avoid). Copy these files into your project, fill in the bodies for the hooks you enable, and point Tracy at the header. + +These are the only categories currently exposed through the custom-platform mechanism. Other platform-specific subsystems (call stack collection, context switch capture, crash handling, system tracing, and so on) are not pluggable this way. If your platform cannot support one of them, disable it at build time using the corresponding \texttt{TRACY\_NO\_*} macros rather than trying to stub it out via the platform header. + +\begin{bclogo}[ +noborder=true, +couleur=black!5, +logo=\bcbombe +]{Important} +The platform header is intended only for the \texttt{TRACY\_HAS\_CUSTOM\_*} hooks and their matching function declarations. Do not use it to set unrelated \texttt{TRACY\_*} options (such as \texttt{TRACY\_ENABLE} or \texttt{TRACY\_ON\_DEMAND}). Some of those are checked in \texttt{Tracy.hpp} before the platform header is included, so the results would be inconsistent depending on which translation unit consults them. Set those options at the build system level instead, as described in section~\ref{initialsetup}. +\end{bclogo} + \subsubsection{Troubleshooting} By default, Tracy's diagnostics will be sent as Message logs (section~\ref{messagelog}) to the server. diff --git a/meson.build b/meson.build index e3c0b264..4afb61f7 100644 --- a/meson.build +++ b/meson.build @@ -21,6 +21,10 @@ if get_option('callstack') > 0 tracy_common_args += ['-DTRACY_CALLSTACK='+get_option('callstack').to_string()] endif +if get_option('platform_header') != '' + tracy_common_args += ['-DTRACY_PLATFORM_HEADER="'+get_option('platform_header')+'"'] +endif + if get_option('no_callstack') tracy_common_args += ['-DTRACY_NO_CALLSTACK'] endif diff --git a/meson.options b/meson.options index 2802db73..55e4fbf1 100644 --- a/meson.options +++ b/meson.options @@ -1,6 +1,7 @@ option('tracy_enable', type : 'boolean', value : false, description : 'Enable profiling', yield: true) option('on_demand', type : 'boolean', value : false, description : 'On-demand profiling') option('callstack', type : 'integer', value : 0, description : 'Enforce callstack collection for tracy zones x frames deep') +option('platform_header', type : 'string', value : '', description : 'Path to a header providing TRACY_HAS_CUSTOM_* hooks for an unsupported platform') option('no_callstack', type : 'boolean', value : false, description : 'Disable all callstack related functionality') option('no_callstack_inlines', type : 'boolean', value : false, description : 'Disables the inline functions in callstacks') option('only_localhost', type : 'boolean', value : false, description : 'Only listen on the localhost interface') diff --git a/public/TracyClient.cpp b/public/TracyClient.cpp index 8e669759..d814a892 100644 --- a/public/TracyClient.cpp +++ b/public/TracyClient.cpp @@ -26,7 +26,9 @@ #include "client/TracySysTime.cpp" #include "client/TracySysTrace.cpp" #include "common/TracySocket.cpp" +#ifndef TRACY_HAS_CUSTOM_ALLOCATOR #include "client/tracy_rpmalloc.cpp" +#endif #include "client/TracyDxt1.cpp" #include "client/TracyAlloc.cpp" #include "client/TracyOverride.cpp" diff --git a/public/client/TracyAlloc.cpp b/public/client/TracyAlloc.cpp index d85cf02b..6acb19e2 100644 --- a/public/client/TracyAlloc.cpp +++ b/public/client/TracyAlloc.cpp @@ -1,6 +1,6 @@ #include "../common/TracyAlloc.hpp" -#ifdef TRACY_USE_RPMALLOC +#if defined TRACY_USE_RPMALLOC || defined TRACY_HAS_CUSTOM_ALLOCATOR #include @@ -24,12 +24,20 @@ tracy_no_inline static void InitAllocatorPlumbing() const auto done = RpInitDone.load( std::memory_order_acquire ); if( !done ) { +#if defined TRACY_HAS_CUSTOM_ALLOCATOR + PlatformAllocatorInit(); +#else rpmalloc_initialize(); +#endif RpInitDone.store( 1, std::memory_order_release ); } RpInitLock.store( 0, std::memory_order_release ); } +#if defined TRACY_HAS_CUSTOM_ALLOCATOR + PlatformAllocatorThreadInit(); +#else rpmalloc_thread_initialize(); +#endif RpThreadInitDone = true; } diff --git a/public/client/TracyMangle.hpp b/public/client/TracyMangle.hpp index 91baf39c..1e7bc8c5 100644 --- a/public/client/TracyMangle.hpp +++ b/public/client/TracyMangle.hpp @@ -43,8 +43,14 @@ #define TRACY_TIMER_FALLBACK_MANGLE _TF0 #endif +#ifdef TRACY_PLATFORM_HEADER +#define TRACY_PLATFORM_HEADER_MANGLE _PH1 +#else +#define TRACY_PLATFORM_HEADER_MANGLE _PH0 +#endif + #define MANGLED_NAME_BASED_ON_CONFIG(base) \ - TracyConcat(TracyConcat(TracyConcat(TracyConcat(TracyConcat(TracyConcat(TracyConcat( \ + TracyConcat(TracyConcat(TracyConcat(TracyConcat(TracyConcat(TracyConcat(TracyConcat(TracyConcat( \ base##_CFG, \ TRACY_ENABLE_MANGLE), \ TRACY_ON_DEMAND_MANGLE), \ @@ -52,6 +58,7 @@ TRACY_MANUAL_LIFETIME_MANGLE), \ TRACY_FIBERS_MANGLE), \ TRACY_DISALLOW_HW_TIMER_MANGLE), \ - TRACY_TIMER_FALLBACK_MANGLE) + TRACY_TIMER_FALLBACK_MANGLE), \ + TRACY_PLATFORM_HEADER_MANGLE) #endif // __TRACYMANGLE_HPP__ diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 9eed5970..05ed9922 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -71,7 +71,6 @@ #include "../common/TracySystem.hpp" #include "../common/TracyYield.hpp" #include "../common/tracy_lz4.hpp" -#include "tracy_rpmalloc.hpp" #include "TracyCallstack.hpp" #include "TracyDebug.hpp" #include "TracyDxt1.hpp" @@ -605,7 +604,11 @@ static const char* GetHostInfo() const char* user = GetUserLogin(); char hostname[512] = {}; +#if defined TRACY_HAS_CUSTOM_USER_INFO + PlatformGetHostname( hostname, sizeof( hostname ) ); +#else gethostname( hostname, sizeof( hostname ) ); +#endif ptr += sprintf( ptr, "User: %s@%s", user, hostname ); const char* fullName = GetUserFullName(); @@ -1284,7 +1287,11 @@ TRACY_API void ShutdownProfiler() s_profilerData->~ProfilerData(); tracy_free( s_profilerData ); s_profilerData = nullptr; +#if defined TRACY_HAS_CUSTOM_ALLOCATOR + PlatformAllocatorFinalize(); +#elif defined TRACY_USE_RPMALLOC rpmalloc_finalize(); +#endif RpThreadInitDone = false; RpInitDone.store( 0, std::memory_order_release ); } @@ -1531,7 +1538,7 @@ Profiler::Profiler() m_safeSendBuffer = (char*)tracy_malloc( SafeSendBufferSize ); -#ifndef _WIN32 +#if !defined _WIN32 && !defined TRACY_HAS_CUSTOM_SAFE_COPY pipe(m_pipe); # if defined __APPLE__ || defined __FreeBSD__ || defined __NetBSD__ || defined __OpenBSD__ || defined __DragonFly__ // FreeBSD/XNU don't have F_SETPIPE_SZ, so use the default @@ -1675,7 +1682,7 @@ Profiler::~Profiler() tracy_free( m_kcore ); #endif -#ifndef _WIN32 +#if !defined _WIN32 && !defined TRACY_HAS_CUSTOM_SAFE_COPY close( m_pipe[0] ); close( m_pipe[1] ); #endif @@ -3349,7 +3356,9 @@ char* Profiler::SafeCopyProlog( const char* data, size_t size ) if( size > SafeSendBufferSize ) buf = (char*)tracy_malloc( size ); -#ifdef _WIN32 +#if defined TRACY_HAS_CUSTOM_SAFE_COPY + success = PlatformSafeMemcpy( buf, data, size ); +#elif defined _WIN32 # ifdef _MSC_VER __try { diff --git a/public/client/TracyProfiler.hpp b/public/client/TracyProfiler.hpp index 6c42d5d5..f1a221a8 100644 --- a/public/client/TracyProfiler.hpp +++ b/public/client/TracyProfiler.hpp @@ -22,6 +22,10 @@ #include "../common/TracyMutex.hpp" #include "../common/TracyProtocol.hpp" +#ifdef TRACY_PLATFORM_HEADER +# include TRACY_PLATFORM_HEADER +#endif + #if defined _WIN32 # include #endif @@ -1122,7 +1126,7 @@ private: #if defined _WIN32 void* m_prevHandler; -#else +#elif !defined TRACY_HAS_CUSTOM_SAFE_COPY int m_pipe[2]; int m_pipeBufSize; #endif diff --git a/public/client/TracyThread.hpp b/public/client/TracyThread.hpp index 5638756a..f95627d4 100644 --- a/public/client/TracyThread.hpp +++ b/public/client/TracyThread.hpp @@ -8,7 +8,7 @@ #endif #ifdef TRACY_MANUAL_LIFETIME -# include "tracy_rpmalloc.hpp" +# include "../common/TracyAlloc.hpp" #endif namespace tracy @@ -24,7 +24,11 @@ public: ~ThreadExitHandler() { #ifdef TRACY_MANUAL_LIFETIME +# if defined TRACY_HAS_CUSTOM_ALLOCATOR + PlatformAllocatorThreadFinalize(); +# elif defined TRACY_USE_RPMALLOC rpmalloc_thread_finalize( 1 ); +# endif RpThreadInitDone = false; #endif } diff --git a/public/common/TracyAlloc.hpp b/public/common/TracyAlloc.hpp index 6e6498f2..52ec2150 100644 --- a/public/common/TracyAlloc.hpp +++ b/public/common/TracyAlloc.hpp @@ -3,17 +3,23 @@ #include +#ifdef TRACY_PLATFORM_HEADER +# include TRACY_PLATFORM_HEADER +#endif + #if defined TRACY_ENABLE && !defined __EMSCRIPTEN__ # include "TracyApi.h" # include "TracyForceInline.hpp" -# include "../client/tracy_rpmalloc.hpp" -# define TRACY_USE_RPMALLOC +# if !defined TRACY_HAS_CUSTOM_ALLOCATOR +# include "../client/tracy_rpmalloc.hpp" +# define TRACY_USE_RPMALLOC +# endif #endif namespace tracy { -#ifdef TRACY_USE_RPMALLOC +#if defined TRACY_USE_RPMALLOC || defined TRACY_HAS_CUSTOM_ALLOCATOR TRACY_API void InitAllocator(); #else static inline void InitAllocator() {} @@ -21,7 +27,10 @@ static inline void InitAllocator() {} static inline void* tracy_malloc( size_t size ) { -#ifdef TRACY_USE_RPMALLOC +#if defined TRACY_HAS_CUSTOM_ALLOCATOR + InitAllocator(); + return PlatformMalloc( size ); +#elif defined TRACY_USE_RPMALLOC InitAllocator(); return rpmalloc( size ); #else @@ -31,7 +40,9 @@ static inline void* tracy_malloc( size_t size ) static inline void* tracy_malloc_fast( size_t size ) { -#ifdef TRACY_USE_RPMALLOC +#if defined TRACY_HAS_CUSTOM_ALLOCATOR + return PlatformMalloc( size ); +#elif defined TRACY_USE_RPMALLOC return rpmalloc( size ); #else return malloc( size ); @@ -40,7 +51,10 @@ static inline void* tracy_malloc_fast( size_t size ) static inline void tracy_free( void* ptr ) { -#ifdef TRACY_USE_RPMALLOC +#if defined TRACY_HAS_CUSTOM_ALLOCATOR + InitAllocator(); + PlatformFree( ptr ); +#elif defined TRACY_USE_RPMALLOC InitAllocator(); rpfree( ptr ); #else @@ -50,7 +64,9 @@ static inline void tracy_free( void* ptr ) static inline void tracy_free_fast( void* ptr ) { -#ifdef TRACY_USE_RPMALLOC +#if defined TRACY_HAS_CUSTOM_ALLOCATOR + PlatformFree( ptr ); +#elif defined TRACY_USE_RPMALLOC rpfree( ptr ); #else free( ptr ); @@ -59,7 +75,10 @@ static inline void tracy_free_fast( void* ptr ) static inline void* tracy_realloc( void* ptr, size_t size ) { -#ifdef TRACY_USE_RPMALLOC +#if defined TRACY_HAS_CUSTOM_ALLOCATOR + InitAllocator(); + return PlatformRealloc( ptr, size ); +#elif defined TRACY_USE_RPMALLOC InitAllocator(); return rprealloc( ptr, size ); #else diff --git a/public/common/TracySystem.cpp b/public/common/TracySystem.cpp index fcd4be47..ca944334 100644 --- a/public/common/TracySystem.cpp +++ b/public/common/TracySystem.cpp @@ -51,6 +51,10 @@ #include "TracySystem.hpp" +#ifdef TRACY_PLATFORM_HEADER +# include TRACY_PLATFORM_HEADER +#endif + #if defined _WIN32 extern "C" typedef HRESULT (WINAPI *t_SetThreadDescription)( HANDLE, PCWSTR ); extern "C" typedef HRESULT (WINAPI *t_GetThreadDescription)( HANDLE, PWSTR* ); @@ -69,7 +73,9 @@ namespace detail TRACY_API uint32_t GetThreadHandleImpl() { -#if defined _WIN32 +#if defined TRACY_HAS_CUSTOM_THREAD_ID + return PlatformGetThreadId(); +#elif defined _WIN32 static_assert( sizeof( decltype( GetCurrentThreadId() ) ) <= sizeof( uint32_t ), "Thread handle too big to fit in protocol" ); return uint32_t( GetCurrentThreadId() ); #elif defined __APPLE__ @@ -341,7 +347,9 @@ TRACY_API const char* GetEnvVar( const char* name ) TRACY_API const char* GetUserLogin() { -#if defined _WIN32 +#if defined TRACY_HAS_CUSTOM_USER_INFO + return PlatformGetUserLogin(); +#elif defined _WIN32 # if defined TRACY_WIN32_NO_DESKTOP return "(?)"; # else @@ -363,7 +371,9 @@ TRACY_API const char* GetUserLogin() TRACY_API const char* GetUserFullName() { -#if defined _WIN32 +#if defined TRACY_HAS_CUSTOM_USER_INFO + return PlatformGetUserFullName(); +#elif defined _WIN32 static char buf[1024]; ULONG size = sizeof( buf ); if( GetUserNameExA( NameDisplay, buf, &size ) ) return buf;