From 92511c9742f6a74d681e48daff16cecd58525e22 Mon Sep 17 00:00:00 2001 From: Matej Mulej Date: Mon, 11 Mar 2024 21:01:25 +0100 Subject: [PATCH 1/5] Add non-C++ lock API --- public/client/TracyProfiler.cpp | 169 ++++++++++++++++++++++++++++++++ 1 file changed, 169 insertions(+) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 5e9110c6..7a97c78c 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -4505,6 +4505,175 @@ TRACY_API void ___tracy_emit_gpu_time_sync_serial( const struct ___tracy_gpu_tim tracy::Profiler::QueueSerialFinish(); } +struct __tracy_lockable_context_data +{ + uint32_t m_id; +#ifdef TRACY_ON_DEMAND + std::atomic m_lockCount; + std::atomic m_active; +#endif +}; + +TRACY_API struct __tracy_lockable_context_data* ___tracy_announce_lockable_ctx(const struct ___tracy_source_location_data* srcloc ) +{ + struct __tracy_lockable_context_data *lockdata = (__tracy_lockable_context_data*)tracy::tracy_malloc( sizeof( __tracy_lockable_context_data ) ); + lockdata->m_id =tracy:: GetLockCounter().fetch_add( 1, std::memory_order_relaxed ); +#ifdef TRACY_ON_DEMAND + new(&lockdata->m_lockCount) std::atomic( 0 ); + new(&lockdata->m_active) std::atomic( false ); +#endif + assert( lockdata->m_id != (std::numeric_limits::max)() ); + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockAnnounce ); + tracy::MemWrite( &item->lockAnnounce.id, lockdata->m_id ); + tracy::MemWrite( &item->lockAnnounce.time, tracy::Profiler::GetTime() ); + tracy::MemWrite( &item->lockAnnounce.lckloc, (uint64_t)srcloc ); + tracy::MemWrite( &item->lockAnnounce.type, tracy::LockType::Lockable ); +#ifdef TRACY_ON_DEMAND + tracy::GetProfiler().DeferItem( *item ); +#endif + tracy::Profiler::QueueSerialFinish(); + + return lockdata; +} + +TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_data* lockdata) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockTerminate ); + tracy::MemWrite( &item->lockTerminate.id, lockdata->m_id ); + tracy::MemWrite( &item->lockTerminate.time, tracy::Profiler::GetTime() ); +#ifdef TRACY_ON_DEMAND + tracy::GetProfiler().DeferItem( *item ); +#endif + tracy::Profiler::QueueSerialFinish(); + +#ifdef TRACY_ON_DEMAND + lockdata->m_lockCount.~atomic(); + lockdata->m_active.~atomic(); +#endif + tracy::tracy_free((void*)lockdata); +} + +TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata) +{ +#ifdef TRACY_ON_DEMAND + bool queue = false; + const auto locks = lockdata->m_lockCount.fetch_add( 1, std::memory_order_relaxed ); + const auto active = lockdata->m_active.load( std::memory_order_relaxed ); + if( locks == 0 || active ) + { + const bool connected = tracy::GetProfiler().IsConnected(); + if( active != connected ) lockdata->m_active.store( connected, std::memory_order_relaxed ); + if( connected ) queue = true; + } + if( !queue ) return false; +#endif + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockWait ); + tracy::MemWrite( &item->lockWait.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->lockWait.id, lockdata->m_id ); + tracy::MemWrite( &item->lockWait.time, tracy::Profiler::GetTime() ); + tracy::Profiler::QueueSerialFinish(); + return true; +} + +TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata) +{ + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockObtain ); + tracy::MemWrite( &item->lockObtain.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->lockObtain.id, lockdata->m_id ); + tracy::MemWrite( &item->lockObtain.time, tracy::Profiler::GetTime() ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_after_unlock_lockable_ctx( struct __tracy_lockable_context_data* lockdata) +{ +#ifdef TRACY_ON_DEMAND + lockdata->m_lockCount.fetch_sub( 1, std::memory_order_relaxed ); + if( !lockdata->m_active.load( std::memory_order_relaxed ) ) return; + if( !tracy::GetProfiler().IsConnected() ) + { + lockdata->m_active.store( false, std::memory_order_relaxed ); + return; + } +#endif + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockRelease ); + tracy::MemWrite( &item->lockRelease.id, lockdata->m_id ); + tracy::MemWrite( &item->lockRelease.time, tracy::Profiler::GetTime() ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_after_try_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata, int acquired ) +{ +#ifdef TRACY_ON_DEMAND + if( !acquired ) return; + + bool queue = false; + const auto locks = lockdata->m_lockCount.fetch_add( 1, std::memory_order_relaxed ); + const auto active = lockdata->m_active.load( std::memory_order_relaxed ); + if( locks == 0 || active ) + { + const bool connected = tracy::GetProfiler().IsConnected(); + if( active != connected ) lockdata->m_active.store( connected, std::memory_order_relaxed ); + if( connected ) queue = true; + } + if( !queue ) return; +#endif + + if( acquired ) + { + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockObtain ); + tracy::MemWrite( &item->lockObtain.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->lockObtain.id, lockdata->m_id ); + tracy::MemWrite( &item->lockObtain.time, tracy::Profiler::GetTime() ); + tracy::Profiler::QueueSerialFinish(); + } +} + +TRACY_API void ___tracy_mark_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const struct ___tracy_source_location_data* srcloc ) +{ +#ifdef TRACY_ON_DEMAND + const auto active = lockdata->m_active.load( std::memory_order_relaxed ); + if( !active ) return; + const auto connected = tracy::GetProfiler().IsConnected(); + if( !connected ) + { + if( active ) lockdata->m_active.store( false, std::memory_order_relaxed ); + return; + } +#endif + + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockMark ); + tracy::MemWrite( &item->lockMark.thread, tracy::GetThreadHandle() ); + tracy::MemWrite( &item->lockMark.id, lockdata->m_id ); + tracy::MemWrite( &item->lockMark.srcloc, (uint64_t)srcloc ); + tracy::Profiler::QueueSerialFinish(); +} + +TRACY_API void ___tracy_custom_name_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const char* name, size_t nameSz ) +{ + assert( nameSz < (std::numeric_limits::max)() ); + auto ptr = (char*)tracy::tracy_malloc( nameSz ); + memcpy( ptr, name, nameSz ); + auto item = tracy::Profiler::QueueSerial(); + tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockName ); + tracy::MemWrite( &item->lockNameFat.id, lockdata->m_id ); + tracy::MemWrite( &item->lockNameFat.name, (uint64_t)ptr ); + tracy::MemWrite( &item->lockNameFat.size, (uint16_t)nameSz ); +#ifdef TRACY_ON_DEMAND + tracy::GetProfiler().DeferItem( *item ); +#endif + tracy::Profiler::QueueSerialFinish(); +} + TRACY_API int ___tracy_connected( void ) { return tracy::GetProfiler().IsConnected(); From 2e7081e52a7bda8546325e5ddaf9cff1e580bb17 Mon Sep 17 00:00:00 2001 From: Matej Mulej Date: Mon, 11 Mar 2024 21:06:11 +0100 Subject: [PATCH 2/5] Expose lock API to TracyC.h --- public/tracy/TracyC.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/public/tracy/TracyC.h b/public/tracy/TracyC.h index 6e62d56a..8ca23ec8 100644 --- a/public/tracy/TracyC.h +++ b/public/tracy/TracyC.h @@ -178,6 +178,8 @@ struct ___tracy_gpu_time_sync_data { uint8_t context; }; +struct __tracy_lockable_context_data; + // Some containers don't support storing const types. // This struct, as visible to user, is immutable, so treat it as if const was declared here. typedef /*const*/ struct ___tracy_c_zone_context TracyCZoneCtx; @@ -228,6 +230,15 @@ TRACY_API void ___tracy_emit_gpu_context_name_serial( const struct ___tracy_gpu_ TRACY_API void ___tracy_emit_gpu_calibration_serial( const struct ___tracy_gpu_calibration_data ); TRACY_API void ___tracy_emit_gpu_time_sync_serial( const struct ___tracy_gpu_time_sync_data ); +TRACY_API struct __tracy_lockable_context_data* ___tracy_announce_lockable_ctx(const struct ___tracy_source_location_data* srcloc ); +TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_data* lockdata); +TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata); +TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata); +TRACY_API void ___tracy_after_unlock_lockable_ctx( struct __tracy_lockable_context_data* lockdata); +TRACY_API void ___tracy_after_try_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata, int acquired ); +TRACY_API void ___tracy_mark_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const struct ___tracy_source_location_data* srcloc ); +TRACY_API void ___tracy_custom_name_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const char* name, size_t nameSz ); + TRACY_API int ___tracy_connected(void); #if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK From 14438be2427d53aac780411a09c36ab77825712a Mon Sep 17 00:00:00 2001 From: Matej Mulej Date: Tue, 12 Mar 2024 20:56:00 +0100 Subject: [PATCH 3/5] Cleanup and define nice C macros for lock API. --- public/client/TracyProfiler.cpp | 10 ++++---- public/tracy/TracyC.h | 41 +++++++++++++++++++++++++-------- 2 files changed, 37 insertions(+), 14 deletions(-) diff --git a/public/client/TracyProfiler.cpp b/public/client/TracyProfiler.cpp index 7a97c78c..b195945b 100644 --- a/public/client/TracyProfiler.cpp +++ b/public/client/TracyProfiler.cpp @@ -4514,7 +4514,7 @@ struct __tracy_lockable_context_data #endif }; -TRACY_API struct __tracy_lockable_context_data* ___tracy_announce_lockable_ctx(const struct ___tracy_source_location_data* srcloc ) +TRACY_API struct __tracy_lockable_context_data* ___tracy_announce_lockable_ctx( const struct ___tracy_source_location_data* srcloc ) { struct __tracy_lockable_context_data *lockdata = (__tracy_lockable_context_data*)tracy::tracy_malloc( sizeof( __tracy_lockable_context_data ) ); lockdata->m_id =tracy:: GetLockCounter().fetch_add( 1, std::memory_order_relaxed ); @@ -4538,7 +4538,7 @@ TRACY_API struct __tracy_lockable_context_data* ___tracy_announce_lockable_ctx(c return lockdata; } -TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_data* lockdata) +TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) { auto item = tracy::Profiler::QueueSerial(); tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockTerminate ); @@ -4556,7 +4556,7 @@ TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_ tracy::tracy_free((void*)lockdata); } -TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata) +TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) { #ifdef TRACY_ON_DEMAND bool queue = false; @@ -4580,7 +4580,7 @@ TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context return true; } -TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata) +TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) { auto item = tracy::Profiler::QueueSerial(); tracy::MemWrite( &item->hdr.type, tracy::QueueType::LockObtain ); @@ -4590,7 +4590,7 @@ TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context tracy::Profiler::QueueSerialFinish(); } -TRACY_API void ___tracy_after_unlock_lockable_ctx( struct __tracy_lockable_context_data* lockdata) +TRACY_API void ___tracy_after_unlock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ) { #ifdef TRACY_ON_DEMAND lockdata->m_lockCount.fetch_sub( 1, std::memory_order_relaxed ); diff --git a/public/tracy/TracyC.h b/public/tracy/TracyC.h index 8ca23ec8..45522f36 100644 --- a/public/tracy/TracyC.h +++ b/public/tracy/TracyC.h @@ -39,6 +39,8 @@ TRACY_API void ___tracy_set_thread_name( const char* name ); typedef const void* TracyCZoneCtx; +typedef const void* TracyCLockCtx; + #define TracyCZone(c,x) #define TracyCZoneN(c,x,y) #define TracyCZoneC(c,x,y) @@ -96,6 +98,16 @@ typedef const void* TracyCZoneCtx; #define TracyCMessageCS(x,y,z,w) #define TracyCMessageLCS(x,y,z) +#define TracyCLockCtx(l) +#define TracyCLockAnnounce(l) +#define TracyCLockTerminate(l) +#define TracyCLockBeforeLock(l) +#define TracyCLockAfterLock(l) +#define TracyCLockAfterUnlock(l) +#define TracyCLockAfterTryLock(l,x) +#define TracyCLockMark(l) +#define TracyCLockCustomName(l,x,y) + #define TracyCIsConnected 0 #define TracyCIsStarted 0 @@ -184,6 +196,7 @@ struct __tracy_lockable_context_data; // This struct, as visible to user, is immutable, so treat it as if const was declared here. typedef /*const*/ struct ___tracy_c_zone_context TracyCZoneCtx; +typedef struct __tracy_lockable_context_data* TracyCLockCtx; #ifdef TRACY_MANUAL_LIFETIME TRACY_API void ___tracy_startup_profiler(void); @@ -230,15 +243,6 @@ TRACY_API void ___tracy_emit_gpu_context_name_serial( const struct ___tracy_gpu_ TRACY_API void ___tracy_emit_gpu_calibration_serial( const struct ___tracy_gpu_calibration_data ); TRACY_API void ___tracy_emit_gpu_time_sync_serial( const struct ___tracy_gpu_time_sync_data ); -TRACY_API struct __tracy_lockable_context_data* ___tracy_announce_lockable_ctx(const struct ___tracy_source_location_data* srcloc ); -TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_data* lockdata); -TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata); -TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata); -TRACY_API void ___tracy_after_unlock_lockable_ctx( struct __tracy_lockable_context_data* lockdata); -TRACY_API void ___tracy_after_try_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata, int acquired ); -TRACY_API void ___tracy_mark_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const struct ___tracy_source_location_data* srcloc ); -TRACY_API void ___tracy_custom_name_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const char* name, size_t nameSz ); - TRACY_API int ___tracy_connected(void); #if defined TRACY_HAS_CALLSTACK && defined TRACY_CALLSTACK @@ -375,6 +379,25 @@ TRACY_API void ___tracy_emit_message_appinfo( const char* txt, size_t size ); # define TracyCMessageLCS( txt, color, depth ) TracyCMessageLC( txt, color ) #endif + +TRACY_API struct __tracy_lockable_context_data* ___tracy_announce_lockable_ctx( const struct ___tracy_source_location_data* srcloc ); +TRACY_API void ___tracy_terminate_lockable_ctx( struct __tracy_lockable_context_data* lockdata ); +TRACY_API int ___tracy_before_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ); +TRACY_API void ___tracy_after_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ); +TRACY_API void ___tracy_after_unlock_lockable_ctx( struct __tracy_lockable_context_data* lockdata ); +TRACY_API void ___tracy_after_try_lock_lockable_ctx( struct __tracy_lockable_context_data* lockdata, int acquired ); +TRACY_API void ___tracy_mark_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const struct ___tracy_source_location_data* srcloc ); +TRACY_API void ___tracy_custom_name_lockable_ctx( struct __tracy_lockable_context_data* lockdata, const char* name, size_t nameSz ); + +#define TracyCLockAnnounce( lock ) static const struct ___tracy_source_location_data TracyConcat(__tracy_source_location,TracyLine) = { NULL, __func__, TracyFile, (uint32_t)TracyLine, 0 }; lock = ___tracy_announce_lockable_ctx( &TracyConcat(__tracy_source_location,TracyLine) ); +#define TracyCLockTerminate( lock ) ___tracy_terminate_lockable_ctx( lock ); +#define TracyCLockBeforeLock( lock ) ___tracy_before_lock_lockable_ctx( lock ); +#define TracyCLockAfterLock( lock ) ___tracy_after_lock_lockable_ctx( lock ); +#define TracyCLockAfterUnlock( lock ) ___tracy_after_unlock_lockable_ctx( lock ); +#define TracyCLockAfterTryLock( lock, acquired ) ___tracy_after_try_lock_lockable_ctx( lock, acquired ); +#define TracyCLockMark( lock ) static const struct ___tracy_source_location_data TracyConcat(__tracy_source_location,TracyLine) = { NULL, __func__, TracyFile, (uint32_t)TracyLine, 0 }; ___tracy_mark_lockable_ctx( lock, &TracyConcat(__tracy_source_location,TracyLine) ); +#define TracyCLockCustomName( lock, name, nameSz ) ___tracy_custom_name_lockable_ctx( lock, name, nameSz ); + #define TracyCIsConnected ___tracy_connected() #ifdef TRACY_FIBERS From 218d90fb3fd36147a8d554fdbb3d2035495c1945 Mon Sep 17 00:00:00 2001 From: Matej Mulej Date: Tue, 12 Mar 2024 20:56:56 +0100 Subject: [PATCH 4/5] Add documentation for the C lock API. --- manual/tracy.tex | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/manual/tracy.tex b/manual/tracy.tex index c35e5d36..ef13d1ff 100644 --- a/manual/tracy.tex +++ b/manual/tracy.tex @@ -2010,6 +2010,55 @@ However, the validation comes with a performance cost, which you may not want to There is no explicit support for transient zones (section~\ref{transientzones}) in the C API macros. However, this functionality can be implemented by following instructions outlined in section~\ref{capibindings}. +\subsubsection{Lock markup} + +Marking locks in the C API is done with the following macros: + +\begin{itemize} +\item \texttt{TracyCLockAnnounce(lock\_ctx)} +\item \texttt{TracyCLockTerminate(lock\_ctx)} +\item \texttt{TracyCLockBeforeLock(lock\_ctx)} +\item \texttt{TracyCLockAfterLock(lock\_ctx)} +\item \texttt{TracyCLockAfterUnlock(lock\_ctx)} +\item \texttt{TracyCLockAfterTryLock(lock\_ctx, acquired)} +\item \texttt{TracyCLockMark(lock\_ctx)} +\item \texttt{TracyCLockCustomName(lock\_ctx, name, size)} +\end{itemize} + +Additionally a lock context has to be defined next to the lock that they will be marking: + +\begin{lstlisting} +TracyCLockCtx tracy_lock_ctx; +HANDLE lock; +\end{lstlisting} + +To initialize the lock context use \texttt{TracyCLockAnnounce}, this should be done when the lock you are marking is initialized/created. When the lock is destroyed use \texttt{TracyCLockTerminate}, this will free the lock context. You can use the \texttt{TracyCLockCustomName} macro to name a lock. + +You must markup both before and after acquiring a lock: + +\begin{lstlisting} +TracyCLockBeforeLock(tracy_lock_ctx); +WaitForSingleObject(lock, INFINITE); +TracyCLockAfterLock(tracy_lock_ctx); +\end{lstlisting} + +If acquiring the lock may fail, you should instead use the \texttt{TracyCLockAfterTryLock} macro: + +\begin{lstlisting} +TracyCLockBeforeLock(tracy_lock_ctx); +int acquired = WaitForSingleObject(lock, 200) == WAIT_OBJECT_0; +TracyCLockAfterTryLock(tracy_lock_ctx, acquired); +\end{lstlisting} + +After you release the lock use the \texttt{TracyCLockAfterUnlock} macro: + +\begin{lstlisting} +ReleaseMutex(lock); +TracyCLockAfterUnlock(tracy_lock_ctx); +\end{lstlisting} + +You can optionally mark the location of where the lock is held by using the \texttt{TracyCLockMark} macro, this should be done after acquiring the lock. + \subsubsection{Memory profiling} Use the following macros in your implementations of \texttt{malloc} and \texttt{free}: From db6128afe2b57c4564ffa2fa07dc7f54d6c69662 Mon Sep 17 00:00:00 2001 From: Matej Mulej Date: Tue, 12 Mar 2024 20:59:52 +0100 Subject: [PATCH 5/5] Grammar. --- manual/tracy.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/tracy.tex b/manual/tracy.tex index ef13d1ff..018e70ad 100644 --- a/manual/tracy.tex +++ b/manual/tracy.tex @@ -2025,7 +2025,7 @@ Marking locks in the C API is done with the following macros: \item \texttt{TracyCLockCustomName(lock\_ctx, name, size)} \end{itemize} -Additionally a lock context has to be defined next to the lock that they will be marking: +Additionally a lock context has to be defined next to the lock that it will be marking: \begin{lstlisting} TracyCLockCtx tracy_lock_ctx;