diff --git a/README.md b/README.md index ebdc336a..2bfd3bd8 100644 --- a/README.md +++ b/README.md @@ -96,7 +96,7 @@ Alternatively, you may want to embed the server in your application, the same wh #### Lua support -To profile Lua code using tracy, include the `tracy/TracyLua.hpp` header file in your Lua wrapper and execute `tracy::LuaRegister( lua_State* )` function to add instrumentation support. In your Lua code, add `tracy.ZoneBegin()` and `tracy.ZoneEnd()` calls to mark execution zones. Double check if you have included all return paths! Use `tracy.ZoneText( text )` to set zone text. Use `tracy.Message( text )` to send messages. +To profile Lua code using tracy, include the `tracy/TracyLua.hpp` header file in your Lua wrapper and execute `tracy::LuaRegister( lua_State* )` function to add instrumentation support. In your Lua code, add `tracy.ZoneBegin()` and `tracy.ZoneEnd()` calls to mark execution zones. Double check if you have included all return paths! Use `tracy.ZoneText( text )` to set zone text. Use `tracy.ZoneName( name )` to set zone name. Use `tracy.Message( text )` to send messages. Even if tracy is disabled, you still have to pay the no-op function call cost. To prevent that you may want to use the `tracy::LuaRemove( char* script )` function, which will replace instrumentation calls with whitespace. This function does nothing if profiler is enabled. diff --git a/TracyLua.hpp b/TracyLua.hpp index b4e507c5..743a5e52 100644 --- a/TracyLua.hpp +++ b/TracyLua.hpp @@ -25,6 +25,8 @@ static inline void LuaRegister( lua_State* L ) lua_pushcfunction( L, detail::noop ); lua_setfield( L, -2, "ZoneText" ); lua_pushcfunction( L, detail::noop ); + lua_setfield( L, -2, "ZoneName" ); + lua_pushcfunction( L, detail::noop ); lua_setfield( L, -2, "Message" ); lua_setglobal( L, "tracy" ); } @@ -173,6 +175,25 @@ static inline int LuaZoneText( lua_State* L ) return 0; } +static inline int LuaZoneName( lua_State* L ) +{ + auto txt = lua_tostring( L, 1 ); + const auto size = strlen( txt ); + + Magic magic; + auto& token = s_token.ptr; + auto ptr = (char*)tracy_malloc( size+1 ); + memcpy( ptr, txt, size ); + ptr[size] = '\0'; + auto& tail = token->get_tail_index(); + auto item = token->enqueue_begin( magic ); + item->hdr.type = QueueType::ZoneNameLiteral; + item->zoneName.thread = GetThreadHandle(); + item->zoneName.name = (uint64_t)ptr; + tail.store( magic + 1, std::memory_order_release ); + return 0; +} + static inline int LuaMessage( lua_State* L ) { auto txt = lua_tostring( L, 1 ); @@ -204,6 +225,8 @@ static inline void LuaRegister( lua_State* L ) lua_setfield( L, -2, "ZoneEnd" ); lua_pushcfunction( L, detail::LuaZoneText ); lua_setfield( L, -2, "ZoneText" ); + lua_pushcfunction( L, detail::LuaZoneName ); + lua_setfield( L, -2, "ZoneName" ); lua_pushcfunction( L, detail::LuaMessage ); lua_setfield( L, -2, "Message" ); lua_setglobal( L, "tracy" ); diff --git a/client/TracyProfiler.cpp b/client/TracyProfiler.cpp index dd5919b9..217170ce 100644 --- a/client/TracyProfiler.cpp +++ b/client/TracyProfiler.cpp @@ -272,6 +272,11 @@ Profiler::DequeueStatus Profiler::Dequeue( moodycamel::ConsumerToken& token ) SendString( ptr, (const char*)ptr, QueueType::CustomStringData ); tracy_free( (void*)ptr ); break; + case QueueType::ZoneNameLiteral: + ptr = item->zoneName.name; + SendString( ptr, (const char*)ptr, QueueType::CustomStringData ); + tracy_free( (void*)ptr ); + break; case QueueType::Message: ptr = item->message.text; SendString( ptr, (const char*)ptr, QueueType::CustomStringData ); diff --git a/common/TracyQueue.hpp b/common/TracyQueue.hpp index 6a4c827c..a10797fb 100644 --- a/common/TracyQueue.hpp +++ b/common/TracyQueue.hpp @@ -20,6 +20,7 @@ enum class QueueType : uint8_t SourceLocationPayload, ZoneText, ZoneName, + ZoneNameLiteral, LockWait, LockObtain, LockRelease, @@ -184,6 +185,7 @@ static const size_t QueueDataSize[] = { sizeof( QueueHeader ) + sizeof( QueueStringTransfer ), // allocated source location payload sizeof( QueueHeader ) + sizeof( QueueZoneText ), sizeof( QueueHeader ) + sizeof( QueueZoneName ), + sizeof( QueueHeader ) + sizeof( QueueZoneName ), // literal sizeof( QueueHeader ) + sizeof( QueueLockWait ), sizeof( QueueHeader ) + sizeof( QueueLockObtain ), sizeof( QueueHeader ) + sizeof( QueueLockRelease ), diff --git a/server/TracyEvent.hpp b/server/TracyEvent.hpp index 0f6ba6d5..cb205332 100644 --- a/server/TracyEvent.hpp +++ b/server/TracyEvent.hpp @@ -14,9 +14,10 @@ struct StringRef { enum Type { Ptr, Idx }; - StringRef() {} + StringRef() : active( 0 ) {} StringRef( Type t, uint64_t data ) : isidx( t == Idx ) + , active( 1 ) { if( isidx ) { @@ -33,13 +34,15 @@ struct StringRef uint64_t strptr; uint64_t stridx; }; - bool isidx; + + uint8_t isidx : 1; + uint8_t active : 1; }; struct TextData { const char* userText; - uint64_t zoneName; // ptr + StringRef zoneName; }; struct SourceLocation diff --git a/server/TracyView.cpp b/server/TracyView.cpp index 47a8c41e..a1c6173a 100644 --- a/server/TracyView.cpp +++ b/server/TracyView.cpp @@ -564,6 +564,9 @@ void View::Process( const QueueItem& ev ) case QueueType::ZoneName: ProcessZoneName( ev.zoneName ); break; + case QueueType::ZoneNameLiteral: + ProcessZoneNameLiteral( ev.zoneName ); + break; case QueueType::LockWait: ProcessLockWait( ev.lockWait ); break; @@ -682,7 +685,20 @@ void View::ProcessZoneName( const QueueZoneName& ev ) auto zone = stack.back(); CheckString( ev.name ); std::lock_guard lock( m_lock ); - GetTextData( *zone )->zoneName = ev.name; + GetTextData( *zone )->zoneName = StringRef( StringRef::Ptr, ev.name ); +} + +void View::ProcessZoneNameLiteral( const QueueZoneName& ev ) +{ + auto& stack = m_zoneStack[ev.thread]; + assert( !stack.empty() ); + auto zone = stack.back(); + auto it = m_pendingCustomStrings.find( ev.name ); + assert( it != m_pendingCustomStrings.end() ); + m_lock.lock(); + GetTextData( *zone )->zoneName = StringRef( StringRef::Idx, it->second.idx ); + m_lock.unlock(); + m_pendingCustomStrings.erase( it ); } void View::ProcessLockWait( const QueueLockWait& ev ) @@ -2092,7 +2108,7 @@ int View::DrawZoneLevel( const Vector& vec, bool hover, double pxns, else { const char* zoneName; - if( ev.text != -1 && GetTextData( ev )->zoneName ) + if( ev.text != -1 && GetTextData( ev )->zoneName.active ) { zoneName = GetString( GetTextData( ev )->zoneName ); } @@ -2105,7 +2121,7 @@ int View::DrawZoneLevel( const Vector& vec, bool hover, double pxns, if( ev.text != -1 ) { auto td = GetTextData( ev ); - if( td->zoneName ) dmul++; + if( td->zoneName.active ) dmul++; if( td->userText ) dmul++; } @@ -2858,7 +2874,7 @@ void View::DrawZoneInfoWindow() ImGui::Separator(); - if( ev.text != -1 && GetTextData( ev )->zoneName ) + if( ev.text != -1 && GetTextData( ev )->zoneName.active ) { ImGui::Text( "Zone name: %s", GetString( GetTextData( ev )->zoneName ) ); dmul++; @@ -2907,7 +2923,7 @@ void View::DrawZoneInfoWindow() for( size_t i=0; izoneName ) + if( cev.text != -1 && GetTextData( cev )->zoneName.active ) { ImGui::Text( "%s", GetString( GetTextData( cev )->zoneName ) ); } @@ -3070,7 +3086,7 @@ void View::ZoneTooltip( const ZoneEvent& ev ) if( ev.text != -1 ) { auto td = GetTextData( ev ); - if( td->zoneName ) dmul++; + if( td->zoneName.active ) dmul++; if( td->userText ) dmul++; } @@ -3081,7 +3097,7 @@ void View::ZoneTooltip( const ZoneEvent& ev ) const char* func; const char* zoneName; - if( ev.text != -1 && GetTextData( ev )->zoneName ) + if( ev.text != -1 && GetTextData( ev )->zoneName.active ) { zoneName = GetString( GetTextData( ev )->zoneName ); func = GetString( srcloc.function ); @@ -3144,7 +3160,6 @@ TextData* View::GetTextData( ZoneEvent& zone ) { auto td = m_slab.Alloc(); td->userText = nullptr; - td->zoneName = 0; zone.text = m_textData.size(); m_textData.push_back( td ); } diff --git a/server/TracyView.hpp b/server/TracyView.hpp index b8be8265..10f71f56 100644 --- a/server/TracyView.hpp +++ b/server/TracyView.hpp @@ -132,6 +132,7 @@ private: void ProcessFrameMark( const QueueFrameMark& ev ); void ProcessZoneText( const QueueZoneText& ev ); void ProcessZoneName( const QueueZoneName& ev ); + void ProcessZoneNameLiteral( const QueueZoneName& ev ); void ProcessLockWait( const QueueLockWait& ev ); void ProcessLockObtain( const QueueLockObtain& ev ); void ProcessLockRelease( const QueueLockRelease& ev );