diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj
index df509ee0..0da1387f 100644
--- a/profiler/build/win32/Tracy.vcxproj
+++ b/profiler/build/win32/Tracy.vcxproj
@@ -146,6 +146,7 @@
+
diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters
index 02e485b6..e5b11729 100644
--- a/profiler/build/win32/Tracy.vcxproj.filters
+++ b/profiler/build/win32/Tracy.vcxproj.filters
@@ -309,6 +309,9 @@
server
+
+ server
+
diff --git a/server/TracyView.cpp b/server/TracyView.cpp
index abc7a3e1..e0d51623 100644
--- a/server/TracyView.cpp
+++ b/server/TracyView.cpp
@@ -58,22 +58,6 @@ namespace tracy
double s_time = 0;
-static inline uint64_t GetThreadBit( uint8_t thread )
-{
- return uint64_t( 1 ) << thread;
-}
-
-static inline bool IsThreadWaiting( uint64_t bitlist, uint64_t threadBit )
-{
- return ( bitlist & threadBit ) != 0;
-}
-
-static inline bool AreOtherWaiting( uint64_t bitlist, uint64_t threadBit )
-{
- return ( bitlist & ~threadBit ) != 0;
-}
-
-
enum { MinVisSize = 3 };
enum { MinFrameSize = 5 };
@@ -3648,864 +3632,6 @@ int View::SkipGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx,
return maxdepth;
}
-enum class LockState
-{
- Nothing,
- HasLock, // green
- HasBlockingLock, // yellow
- WaitLock // red
-};
-
-static Vector::const_iterator GetNextLockEvent( const Vector::const_iterator& it, const Vector::const_iterator& end, LockState& nextState, uint64_t threadBit )
-{
- auto next = it;
- next++;
-
- switch( nextState )
- {
- case LockState::Nothing:
- while( next < end )
- {
- if( next->lockCount != 0 )
- {
- if( GetThreadBit( next->lockingThread ) == threadBit )
- {
- nextState = AreOtherWaiting( next->waitList, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
- break;
- }
- else if( IsThreadWaiting( next->waitList, threadBit ) )
- {
- nextState = LockState::WaitLock;
- break;
- }
- }
- next++;
- }
- break;
- case LockState::HasLock:
- while( next < end )
- {
- if( next->lockCount == 0 )
- {
- nextState = LockState::Nothing;
- break;
- }
- if( next->waitList != 0 )
- {
- if( AreOtherWaiting( next->waitList, threadBit ) )
- {
- nextState = LockState::HasBlockingLock;
- }
- break;
- }
- if( next->waitList != it->waitList || next->lockCount != it->lockCount )
- {
- break;
- }
- next++;
- }
- break;
- case LockState::HasBlockingLock:
- while( next < end )
- {
- if( next->lockCount == 0 )
- {
- nextState = LockState::Nothing;
- break;
- }
- if( next->waitList != it->waitList || next->lockCount != it->lockCount )
- {
- break;
- }
- next++;
- }
- break;
- case LockState::WaitLock:
- while( next < end )
- {
- if( GetThreadBit( next->lockingThread ) == threadBit )
- {
- nextState = AreOtherWaiting( next->waitList, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
- break;
- }
- if( next->lockingThread != it->lockingThread )
- {
- break;
- }
- if( next->lockCount == 0 )
- {
- break;
- }
- next++;
- }
- break;
- default:
- assert( false );
- break;
- }
-
- return next;
-}
-
-static Vector::const_iterator GetNextLockEventShared( const Vector::const_iterator& it, const Vector::const_iterator& end, LockState& nextState, uint64_t threadBit )
-{
- const auto itptr = (const LockEventShared*)(const LockEvent*)it->ptr;
- auto next = it;
- next++;
-
- switch( nextState )
- {
- case LockState::Nothing:
- while( next < end )
- {
- const auto ptr = (const LockEventShared*)(const LockEvent*)next->ptr;
- if( next->lockCount != 0 )
- {
- const auto wait = next->waitList | ptr->waitShared;
- if( GetThreadBit( next->lockingThread ) == threadBit )
- {
- nextState = AreOtherWaiting( wait, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
- break;
- }
- else if( IsThreadWaiting( wait, threadBit ) )
- {
- nextState = LockState::WaitLock;
- break;
- }
- }
- else if( IsThreadWaiting( ptr->sharedList, threadBit ) )
- {
- nextState = ( next->waitList != 0 ) ? LockState::HasBlockingLock : LockState::HasLock;
- break;
- }
- else if( ptr->sharedList != 0 && IsThreadWaiting( next->waitList, threadBit ) )
- {
- nextState = LockState::WaitLock;
- break;
- }
- next++;
- }
- break;
- case LockState::HasLock:
- while( next < end )
- {
- const auto ptr = (const LockEventShared*)(const LockEvent*)next->ptr;
- if( next->lockCount == 0 && !IsThreadWaiting( ptr->sharedList, threadBit ) )
- {
- nextState = LockState::Nothing;
- break;
- }
- if( next->waitList != 0 )
- {
- if( AreOtherWaiting( next->waitList, threadBit ) )
- {
- nextState = LockState::HasBlockingLock;
- }
- break;
- }
- else if( !IsThreadWaiting( ptr->sharedList, threadBit ) && ptr->waitShared != 0 )
- {
- nextState = LockState::HasBlockingLock;
- break;
- }
- if( next->waitList != it->waitList || ptr->waitShared != itptr->waitShared || next->lockCount != it->lockCount || ptr->sharedList != itptr->sharedList )
- {
- break;
- }
- next++;
- }
- break;
- case LockState::HasBlockingLock:
- while( next < end )
- {
- const auto ptr = (const LockEventShared*)(const LockEvent*)next->ptr;
- if( next->lockCount == 0 && !IsThreadWaiting( ptr->sharedList, threadBit ) )
- {
- nextState = LockState::Nothing;
- break;
- }
- if( next->waitList != it->waitList || ptr->waitShared != itptr->waitShared || next->lockCount != it->lockCount || ptr->sharedList != itptr->sharedList )
- {
- break;
- }
- next++;
- }
- break;
- case LockState::WaitLock:
- while( next < end )
- {
- const auto ptr = (const LockEventShared*)(const LockEvent*)next->ptr;
- if( GetThreadBit( next->lockingThread ) == threadBit )
- {
- const auto wait = next->waitList | ptr->waitShared;
- nextState = AreOtherWaiting( wait, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
- break;
- }
- if( IsThreadWaiting( ptr->sharedList, threadBit ) )
- {
- nextState = ( next->waitList != 0 ) ? LockState::HasBlockingLock : LockState::HasLock;
- break;
- }
- if( next->lockingThread != it->lockingThread )
- {
- break;
- }
- if( next->lockCount == 0 && !IsThreadWaiting( ptr->waitShared, threadBit ) )
- {
- break;
- }
- next++;
- }
- break;
- default:
- assert( false );
- break;
- }
-
- return next;
-}
-
-static LockState CombineLockState( LockState state, LockState next )
-{
- return (LockState)std::max( (int)state, (int)next );
-}
-
-void View::DrawLockHeader( uint32_t id, const LockMap& lockmap, const SourceLocation& srcloc, bool hover, ImDrawList* draw, const ImVec2& wpos, float w, float ty, float offset, uint8_t tid )
-{
- char buf[1024];
- if( lockmap.customName.Active() )
- {
- sprintf( buf, "%" PRIu32 ": %s", id, m_worker.GetString( lockmap.customName ) );
- }
- else
- {
- sprintf( buf, "%" PRIu32 ": %s", id, m_worker.GetString( srcloc.function ) );
- }
- DrawTextContrast( draw, wpos + ImVec2( 0, offset ), 0xFF8888FF, buf );
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty + 1 ) ) )
- {
- m_lockHoverHighlight = id;
-
- if( ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( ty + ImGui::CalcTextSize( buf ).x, offset + ty + 1 ) ) )
- {
- const auto& range = lockmap.range[tid];
- const auto activity = range.end - range.start;
- const auto traceLen = m_worker.GetLastTime();
-
- int64_t timeAnnounce = lockmap.timeAnnounce;
- int64_t timeTerminate = lockmap.timeTerminate;
- if( !lockmap.timeline.empty() )
- {
- if( timeAnnounce <= 0 )
- {
- timeAnnounce = lockmap.timeline.front().ptr->Time();
- }
- if( timeTerminate <= 0 )
- {
- timeTerminate = lockmap.timeline.back().ptr->Time();
- }
- }
- const auto lockLen = timeTerminate - timeAnnounce;
-
- ImGui::BeginTooltip();
- switch( lockmap.type )
- {
- case LockType::Lockable:
- TextFocused( "Type:", "lockable" );
- break;
- case LockType::SharedLockable:
- TextFocused( "Type:", "shared lockable" );
- break;
- default:
- assert( false );
- break;
- }
- ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
- ImGui::Separator();
- TextFocused( ICON_FA_RANDOM " Appeared at", TimeToString( range.start ) );
- TextFocused( ICON_FA_RANDOM " Last event at", TimeToString( range.end ) );
- TextFocused( ICON_FA_RANDOM " Activity time:", TimeToString( activity ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%.2f%% of lock lifetime)", activity / double( lockLen ) * 100 );
- ImGui::Separator();
- TextFocused( "Announce time:", TimeToString( timeAnnounce ) );
- TextFocused( "Terminate time:", TimeToString( timeTerminate ) );
- TextFocused( "Lifetime:", TimeToString( lockLen ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%.2f%% of trace time)", lockLen / double( traceLen ) * 100 );
- ImGui::Separator();
- TextDisabledUnformatted( "Thread list:" );
- ImGui::Indent( ty );
- for( const auto& t : lockmap.threadList )
- {
- SmallColorBox( GetThreadColor( t, 0 ) );
- ImGui::SameLine();
- ImGui::TextUnformatted( m_worker.GetThreadName( t ) );
- }
- ImGui::Unindent( ty );
- ImGui::Separator();
- TextFocused( "Lock events:", RealToString( lockmap.timeline.size() ) );
- ImGui::EndTooltip();
-
- if( IsMouseClicked( 0 ) )
- {
- m_lockInfoWindow = id;
- }
- if( IsMouseClicked( 2 ) )
- {
- ZoomToRange( range.start, range.end );
- }
- }
- }
-}
-
-int View::DrawLocks( uint64_t tid, bool hover, double pxns, const ImVec2& wpos, int _offset, LockHighlight& highlight, float yMin, float yMax )
-{
- const auto delay = m_worker.GetDelay();
- const auto resolution = m_worker.GetResolution();
- const auto w = ImGui::GetContentRegionAvail().x - 1;
- const auto ty = ImGui::GetTextLineHeight();
- const auto ostep = ty + 1;
- auto draw = ImGui::GetWindowDrawList();
- const auto dsz = delay * pxns;
- const auto rsz = resolution * pxns;
- const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
-
- const auto ty025 = round( ty * 0.25f );
- const auto ty05 = round( ty * 0.5f );
- const auto ty075 = round( ty * 0.75f );
-
- int cnt = 0;
- for( const auto& v : m_worker.GetLockMap() )
- {
- const auto& lockmap = *v.second;
- if( !lockmap.valid || !Vis( &lockmap ).visible ) continue;
- if( m_vd.onlyContendedLocks && ( lockmap.threadList.size() == 1 || !lockmap.isContended ) && m_lockInfoWindow != v.first ) continue;
-
- auto it = lockmap.threadMap.find( tid );
- if( it == lockmap.threadMap.end() ) continue;
-
- const auto offset = _offset + ostep * cnt;
-
- const auto& range = lockmap.range[it->second];
- const auto& tl = lockmap.timeline;
- assert( !tl.empty() );
- if( range.start > m_vd.zvEnd || range.end < m_vd.zvStart )
- {
- if( m_lockInfoWindow == v.first )
- {
- draw->AddRectFilled( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x2288DD88 );
- draw->AddRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x4488DD88 );
- DrawLockHeader( v.first, lockmap, m_worker.GetSourceLocation( lockmap.srcloc ), hover, draw, wpos, w, ty, offset, it->second );
- cnt++;
- }
-
- continue;
- }
-
- auto GetNextLockFunc = lockmap.type == LockType::Lockable ? GetNextLockEvent : GetNextLockEventShared;
-
- const auto thread = it->second;
- const auto threadBit = GetThreadBit( thread );
-
- auto vbegin = std::lower_bound( tl.begin(), tl.end(), std::max( range.start, m_vd.zvStart - delay ), [] ( const auto& l, const auto& r ) { return l.ptr->Time() < r; } );
- const auto vend = std::lower_bound( vbegin, tl.end(), std::min( range.end, m_vd.zvEnd + resolution ), [] ( const auto& l, const auto& r ) { return l.ptr->Time() < r; } );
-
- if( vbegin > tl.begin() ) vbegin--;
-
- LockState state = LockState::Nothing;
- if( lockmap.type == LockType::Lockable )
- {
- if( vbegin->lockCount != 0 )
- {
- if( vbegin->lockingThread == thread )
- {
- state = AreOtherWaiting( vbegin->waitList, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
- }
- else if( IsThreadWaiting( vbegin->waitList, threadBit ) )
- {
- state = LockState::WaitLock;
- }
- }
- }
- else
- {
- auto ptr = (const LockEventShared*)(const LockEvent*)vbegin->ptr;
- if( vbegin->lockCount != 0 )
- {
- if( vbegin->lockingThread == thread )
- {
- state = ( AreOtherWaiting( vbegin->waitList, threadBit ) || AreOtherWaiting( ptr->waitShared, threadBit ) ) ? LockState::HasBlockingLock : LockState::HasLock;
- }
- else if( IsThreadWaiting( vbegin->waitList, threadBit ) || IsThreadWaiting( ptr->waitShared, threadBit ) )
- {
- state = LockState::WaitLock;
- }
- }
- else if( IsThreadWaiting( ptr->sharedList, threadBit ) )
- {
- state = vbegin->waitList != 0 ? LockState::HasBlockingLock : LockState::HasLock;
- }
- else if( ptr->sharedList != 0 && IsThreadWaiting( vbegin->waitList, threadBit ) )
- {
- state = LockState::WaitLock;
- }
- }
-
- const auto yPos = wpos.y + offset;
- if( yPos + ostep >= yMin && yPos <= yMax )
- {
- bool drawn = false;
- const auto& srcloc = m_worker.GetSourceLocation( lockmap.srcloc );
-
- double pxend = 0;
- for(;;)
- {
- if( m_vd.onlyContendedLocks )
- {
- while( vbegin < vend && ( state == LockState::Nothing || state == LockState::HasLock ) )
- {
- vbegin = GetNextLockFunc( vbegin, vend, state, threadBit );
- }
- }
- else
- {
- while( vbegin < vend && state == LockState::Nothing )
- {
- vbegin = GetNextLockFunc( vbegin, vend, state, threadBit );
- }
- }
- if( vbegin >= vend ) break;
-
- assert( state != LockState::Nothing && ( !m_vd.onlyContendedLocks || state != LockState::HasLock ) );
- drawn = true;
-
- LockState drawState = state;
- auto next = GetNextLockFunc( vbegin, vend, state, threadBit );
-
- const auto t0 = vbegin->ptr->Time();
- int64_t t1 = next == tl.end() ? m_worker.GetLastTime() : next->ptr->Time();
- const auto px0 = std::max( pxend, ( t0 - m_vd.zvStart ) * pxns );
- auto tx0 = px0;
- double px1 = ( t1 - m_vd.zvStart ) * pxns;
- uint64_t condensed = 0;
-
- if( m_vd.onlyContendedLocks )
- {
- for(;;)
- {
- if( next >= vend || px1 - tx0 > MinVisSize ) break;
- auto n = next;
- auto ns = state;
- while( n < vend && ( ns == LockState::Nothing || ns == LockState::HasLock ) )
- {
- n = GetNextLockFunc( n, vend, ns, threadBit );
- }
- if( n >= vend ) break;
- if( n == next )
- {
- n = GetNextLockFunc( n, vend, ns, threadBit );
- }
- drawState = CombineLockState( drawState, state );
- condensed++;
- const auto t2 = n == tl.end() ? m_worker.GetLastTime() : n->ptr->Time();
- const auto px2 = ( t2 - m_vd.zvStart ) * pxns;
- if( px2 - px1 > MinVisSize ) break;
- if( drawState != ns && px2 - px0 > MinVisSize && !( ns == LockState::Nothing || ns == LockState::HasLock ) ) break;
- t1 = t2;
- tx0 = px1;
- px1 = px2;
- next = n;
- state = ns;
- }
- }
- else
- {
- for(;;)
- {
- if( next >= vend || px1 - tx0 > MinVisSize ) break;
- auto n = next;
- auto ns = state;
- while( n < vend && ns == LockState::Nothing )
- {
- n = GetNextLockFunc( n, vend, ns, threadBit );
- }
- if( n >= vend ) break;
- if( n == next )
- {
- n = GetNextLockFunc( n, vend, ns, threadBit );
- }
- drawState = CombineLockState( drawState, state );
- condensed++;
- const auto t2 = n == tl.end() ? m_worker.GetLastTime() : n->ptr->Time();
- const auto px2 = ( t2 - m_vd.zvStart ) * pxns;
- if( px2 - px1 > MinVisSize ) break;
- if( drawState != ns && px2 - px0 > MinVisSize && ns != LockState::Nothing ) break;
- t1 = t2;
- tx0 = px1;
- px1 = px2;
- next = n;
- state = ns;
- }
- }
-
- pxend = std::max( { px1, px0+MinVisSize, px0 + pxns * 0.5 } );
-
- bool itemHovered = hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( pxend, double( w + 10 ) ), offset + ty + 1 ) );
- if( itemHovered )
- {
- if( IsMouseClicked( 0 ) )
- {
- m_lockInfoWindow = v.first;
- }
- if( IsMouseClicked( 2 ) )
- {
- ZoomToRange( t0, t1 );
- }
-
- if( condensed > 1 )
- {
- ImGui::BeginTooltip();
- TextFocused( "Multiple lock events:", RealToString( condensed ) );
- ImGui::EndTooltip();
- }
- else
- {
- highlight.blocked = drawState == LockState::HasBlockingLock;
- if( !highlight.blocked )
- {
- highlight.id = v.first;
- highlight.begin = t0;
- highlight.end = t1;
- highlight.thread = thread;
- highlight.blocked = false;
- }
- else
- {
- auto b = vbegin;
- while( b != tl.begin() )
- {
- if( b->lockingThread != vbegin->lockingThread )
- {
- break;
- }
- b--;
- }
- b++;
- highlight.begin = b->ptr->Time();
-
- auto e = next;
- while( e != tl.end() )
- {
- if( e->lockingThread != next->lockingThread )
- {
- highlight.id = v.first;
- highlight.end = e->ptr->Time();
- highlight.thread = thread;
- break;
- }
- e++;
- }
- }
-
- ImGui::BeginTooltip();
- if( v.second->customName.Active() )
- {
- ImGui::Text( "Lock #%" PRIu32 ": %s", v.first, m_worker.GetString( v.second->customName ) );
- }
- else
- {
- ImGui::Text( "Lock #%" PRIu32 ": %s", v.first, m_worker.GetString( srcloc.function ) );
- }
- ImGui::Separator();
- ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
- TextFocused( "Time:", TimeToString( t1 - t0 ) );
- ImGui::Separator();
-
- int16_t markloc = 0;
- auto it = vbegin;
- for(;;)
- {
- if( it->ptr->thread == thread )
- {
- if( ( it->lockingThread == thread || IsThreadWaiting( it->waitList, threadBit ) ) && it->ptr->SrcLoc() != 0 )
- {
- markloc = it->ptr->SrcLoc();
- break;
- }
- }
- if( it == tl.begin() ) break;
- --it;
- }
- if( markloc != 0 )
- {
- const auto& marklocdata = m_worker.GetSourceLocation( markloc );
- ImGui::TextUnformatted( "Lock event location:" );
- ImGui::TextUnformatted( m_worker.GetString( marklocdata.function ) );
- ImGui::TextUnformatted( LocationToString( m_worker.GetString( marklocdata.file ), marklocdata.line ) );
- ImGui::Separator();
- }
-
- if( lockmap.type == LockType::Lockable )
- {
- switch( drawState )
- {
- case LockState::HasLock:
- if( vbegin->lockCount == 1 )
- {
- ImGui::Text( "Thread \"%s\" has lock. No other threads are waiting.", m_worker.GetThreadName( tid ) );
- }
- else
- {
- ImGui::Text( "Thread \"%s\" has %i locks. No other threads are waiting.", m_worker.GetThreadName( tid ), vbegin->lockCount );
- }
- if( vbegin->waitList != 0 )
- {
- assert( !AreOtherWaiting( next->waitList, threadBit ) );
- ImGui::TextUnformatted( "Recursive lock acquire in thread." );
- }
- break;
- case LockState::HasBlockingLock:
- {
- if( vbegin->lockCount == 1 )
- {
- ImGui::Text( "Thread \"%s\" has lock. Blocked threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), TracyCountBits( vbegin->waitList ) );
- }
- else
- {
- ImGui::Text( "Thread \"%s\" has %i locks. Blocked threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), vbegin->lockCount, TracyCountBits( vbegin->waitList ) );
- }
- auto waitList = vbegin->waitList;
- int t = 0;
- ImGui::Indent( ty );
- while( waitList != 0 )
- {
- if( waitList & 0x1 )
- {
- ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
- }
- waitList >>= 1;
- t++;
- }
- ImGui::Unindent( ty );
- break;
- }
- case LockState::WaitLock:
- {
- if( vbegin->lockCount > 0 )
- {
- ImGui::Text( "Thread \"%s\" is blocked by other thread:", m_worker.GetThreadName( tid ) );
- }
- else
- {
- ImGui::Text( "Thread \"%s\" waits to obtain lock after release by thread:", m_worker.GetThreadName( tid ) );
- }
- ImGui::Indent( ty );
- ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[vbegin->lockingThread] ) );
- ImGui::Unindent( ty );
- break;
- }
- default:
- assert( false );
- break;
- }
- }
- else
- {
- const auto ptr = (const LockEventShared*)(const LockEvent*)vbegin->ptr;
- switch( drawState )
- {
- case LockState::HasLock:
- assert( vbegin->waitList == 0 );
- if( ptr->sharedList == 0 )
- {
- assert( vbegin->lockCount == 1 );
- ImGui::Text( "Thread \"%s\" has lock. No other threads are waiting.", m_worker.GetThreadName( tid ) );
- }
- else if( TracyCountBits( ptr->sharedList ) == 1 )
- {
- ImGui::Text( "Thread \"%s\" has a sole shared lock. No other threads are waiting.", m_worker.GetThreadName( tid ) );
- }
- else
- {
- ImGui::Text( "Thread \"%s\" has shared lock. No other threads are waiting.", m_worker.GetThreadName( tid ) );
- ImGui::Text( "Threads sharing the lock (%" PRIu64 "):", TracyCountBits( ptr->sharedList ) - 1 );
- auto sharedList = ptr->sharedList;
- int t = 0;
- ImGui::Indent( ty );
- while( sharedList != 0 )
- {
- if( sharedList & 0x1 && t != thread )
- {
- ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
- }
- sharedList >>= 1;
- t++;
- }
- ImGui::Unindent( ty );
- }
- break;
- case LockState::HasBlockingLock:
- {
- if( ptr->sharedList == 0 )
- {
- assert( vbegin->lockCount == 1 );
- ImGui::Text( "Thread \"%s\" has lock. Blocked threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), TracyCountBits( vbegin->waitList ) + TracyCountBits( ptr->waitShared ) );
- }
- else if( TracyCountBits( ptr->sharedList ) == 1 )
- {
- ImGui::Text( "Thread \"%s\" has a sole shared lock. Blocked threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), TracyCountBits( vbegin->waitList ) + TracyCountBits( ptr->waitShared ) );
- }
- else
- {
- ImGui::Text( "Thread \"%s\" has shared lock.", m_worker.GetThreadName( tid ) );
- ImGui::Text( "Threads sharing the lock (%" PRIu64 "):", TracyCountBits( ptr->sharedList ) - 1 );
- auto sharedList = ptr->sharedList;
- int t = 0;
- ImGui::Indent( ty );
- while( sharedList != 0 )
- {
- if( sharedList & 0x1 && t != thread )
- {
- ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
- }
- sharedList >>= 1;
- t++;
- }
- ImGui::Unindent( ty );
- ImGui::Text( "Blocked threads (%" PRIu64 "):", TracyCountBits( vbegin->waitList ) + TracyCountBits( ptr->waitShared ) );
- }
-
- auto waitList = vbegin->waitList;
- int t = 0;
- ImGui::Indent( ty );
- while( waitList != 0 )
- {
- if( waitList & 0x1 )
- {
- ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
- }
- waitList >>= 1;
- t++;
- }
- auto waitShared = ptr->waitShared;
- t = 0;
- while( waitShared != 0 )
- {
- if( waitShared & 0x1 )
- {
- ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
- }
- waitShared >>= 1;
- t++;
- }
- ImGui::Unindent( ty );
- break;
- }
- case LockState::WaitLock:
- {
- assert( vbegin->lockCount == 0 || vbegin->lockCount == 1 );
- if( vbegin->lockCount != 0 || ptr->sharedList != 0 )
- {
- ImGui::Text( "Thread \"%s\" is blocked by other threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), vbegin->lockCount + TracyCountBits( ptr->sharedList ) );
- }
- else
- {
- ImGui::Text( "Thread \"%s\" waits to obtain lock after release by thread:", m_worker.GetThreadName( tid ) );
- }
- ImGui::Indent( ty );
- if( vbegin->lockCount != 0 )
- {
- ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[vbegin->lockingThread] ) );
- }
- auto sharedList = ptr->sharedList;
- int t = 0;
- while( sharedList != 0 )
- {
- if( sharedList & 0x1 )
- {
- ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
- }
- sharedList >>= 1;
- t++;
- }
- ImGui::Unindent( ty );
- break;
- }
- default:
- assert( false );
- break;
- }
- }
- ImGui::EndTooltip();
- }
- }
-
- const auto cfilled = drawState == LockState::HasLock ? 0xFF228A22 : ( drawState == LockState::HasBlockingLock ? 0xFF228A8A : 0xFF2222BD );
- draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( pxend, double( w + 10 ) ), offset + ty ), cfilled );
- if( m_lockHighlight.thread != thread && ( drawState == LockState::HasBlockingLock ) != m_lockHighlight.blocked && next != tl.end() && m_lockHighlight.id == int64_t( v.first ) && m_lockHighlight.begin <= vbegin->ptr->Time() && m_lockHighlight.end >= next->ptr->Time() )
- {
- const auto t = uint8_t( ( sin( std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch() ).count() * 0.01 ) * 0.5 + 0.5 ) * 255 );
- draw->AddRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( pxend, double( w + 10 ) ), offset + ty ), 0x00FFFFFF | ( t << 24 ), 0.f, -1, 2.f );
- }
- else if( condensed == 0 )
- {
- const auto coutline = drawState == LockState::HasLock ? 0xFF3BA33B : ( drawState == LockState::HasBlockingLock ? 0xFF3BA3A3 : 0xFF3B3BD6 );
- draw->AddRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( pxend, double( w + 10 ) ), offset + ty ), coutline );
- }
- else if( condensed > 1 )
- {
- DrawZigZag( draw, wpos + ImVec2( 0, offset + ty05 ), px0, pxend, ty025, DarkenColor( cfilled ) );
- }
-
- const auto rx0 = ( t0 - m_vd.zvStart ) * pxns;
- if( dsz >= MinVisSize )
- {
- draw->AddRectFilled( wpos + ImVec2( rx0, offset ), wpos + ImVec2( std::min( rx0+dsz, px1 ), offset + ty ), 0x882222DD );
- }
- if( rsz >= MinVisSize )
- {
- DrawLine( draw, dpos + ImVec2( rx0 + rsz, offset + ty05 ), dpos + ImVec2( rx0 - rsz, offset + ty05 ), 0xAAFFFFFF );
- DrawLine( draw, dpos + ImVec2( rx0 + rsz, offset + ty025 ), dpos + ImVec2( rx0 + rsz, offset + ty075 ), 0xAAFFFFFF );
- DrawLine( draw, dpos + ImVec2( rx0 - rsz, offset + ty025 ), dpos + ImVec2( rx0 - rsz, offset + ty075 ), 0xAAFFFFFF );
-
- DrawLine( draw, dpos + ImVec2( px1 + rsz, offset + ty05 ), dpos + ImVec2( px1 - rsz, offset + ty05 ), 0xAAFFFFFF );
- DrawLine( draw, dpos + ImVec2( px1 + rsz, offset + ty025 ), dpos + ImVec2( px1 + rsz, offset + ty075 ), 0xAAFFFFFF );
- DrawLine( draw, dpos + ImVec2( px1 - rsz, offset + ty025 ), dpos + ImVec2( px1 - rsz, offset + ty075 ), 0xAAFFFFFF );
- }
-
- vbegin = next;
- }
-
- if( drawn || m_lockInfoWindow == v.first )
- {
- if( m_lockInfoWindow == v.first )
- {
- draw->AddRectFilled( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x2288DD88 );
- draw->AddRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x4488DD88 );
- }
- else if( m_lockHoverHighlight == v.first )
- {
- draw->AddRectFilled( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x228888DD );
- draw->AddRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x448888DD );
- }
-
- DrawLockHeader( v.first, lockmap, srcloc, hover, draw, wpos, w, ty, offset, it->second );
- cnt++;
- }
- }
- else
- {
- while( vbegin < vend && ( state == LockState::Nothing || ( m_vd.onlyContendedLocks && state == LockState::HasLock ) ) )
- {
- vbegin = GetNextLockFunc( vbegin, vend, state, threadBit );
- }
- if( vbegin < vend ) cnt++;
- }
- }
- return cnt;
-}
-
void View::DrawHistogramMinMaxLabel( ImDrawList* draw, int64_t tmin, int64_t tmax, ImVec2 wpos, float w, float ty )
{
const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
@@ -4540,175 +3666,6 @@ void View::DrawTextEditor()
if( !show ) m_sourceViewFile = nullptr;
}
-void View::DrawLockInfoWindow()
-{
- bool visible = true;
- ImGui::Begin( "Lock info", &visible, ImGuiWindowFlags_AlwaysAutoResize );
- if( !ImGui::GetCurrentWindowRead()->SkipItems )
- {
- auto it = m_worker.GetLockMap().find( m_lockInfoWindow );
- assert( it != m_worker.GetLockMap().end() );
- const auto& lock = *it->second;
- const auto& srcloc = m_worker.GetSourceLocation( lock.srcloc );
- auto fileName = m_worker.GetString( srcloc.file );
-
- int64_t timeAnnounce = lock.timeAnnounce;
- int64_t timeTerminate = lock.timeTerminate;
- if( !lock.timeline.empty() )
- {
- if( timeAnnounce <= 0 )
- {
- timeAnnounce = lock.timeline.front().ptr->Time();
- }
- if( timeTerminate <= 0 )
- {
- timeTerminate = lock.timeline.back().ptr->Time();
- }
- }
-
- bool waitState = false;
- bool holdState = false;
- int64_t waitStartTime = 0;
- int64_t holdStartTime = 0;
- int64_t waitTotalTime = 0;
- int64_t holdTotalTime = 0;
- uint32_t maxWaitingThreads = 0;
- for( auto& v : lock.timeline )
- {
- if( holdState )
- {
- if( v.lockCount == 0 )
- {
- holdTotalTime += v.ptr->Time() - holdStartTime;
- holdState = false;
- }
- }
- else
- {
- if( v.lockCount != 0 )
- {
- holdStartTime = v.ptr->Time();
- holdState = true;
- }
- }
- if( waitState )
- {
- if( v.waitList == 0 )
- {
- waitTotalTime += v.ptr->Time() - waitStartTime;
- waitState = false;
- }
- else
- {
- maxWaitingThreads = std::max( maxWaitingThreads, TracyCountBits( v.waitList ) );
- }
- }
- else
- {
- if( v.waitList != 0 )
- {
- waitStartTime = v.ptr->Time();
- waitState = true;
- maxWaitingThreads = std::max( maxWaitingThreads, TracyCountBits( v.waitList ) );
- }
- }
- }
-
- ImGui::PushFont( m_bigFont );
- if( lock.customName.Active() )
- {
- ImGui::Text( "Lock #%" PRIu32 ": %s", m_lockInfoWindow, m_worker.GetString( lock.customName ) );
- }
- else
- {
- ImGui::Text( "Lock #%" PRIu32 ": %s", m_lockInfoWindow, m_worker.GetString( srcloc.function ) );
- }
- ImGui::PopFont();
- if( lock.customName.Active() )
- {
- TextFocused( "Name:", m_worker.GetString( srcloc.function ) );
- }
- TextDisabledUnformatted( "Location:" );
- if( m_lockInfoAnim.Match( m_lockInfoWindow ) )
- {
- const auto time = m_lockInfoAnim.Time();
- const auto indentVal = sin( time * 60.f ) * 10.f * time;
- ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
- }
- else
- {
- ImGui::SameLine();
- }
- ImGui::TextUnformatted( LocationToString( fileName, srcloc.line ) );
- if( ImGui::IsItemClicked( 1 ) )
- {
- if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
- {
- ViewSource( fileName, srcloc.line );
- }
- else
- {
- m_lockInfoAnim.Enable( m_lockInfoWindow, 0.5f );
- }
- }
- ImGui::Separator();
-
- switch( lock.type )
- {
- case LockType::Lockable:
- TextFocused( "Type:", "lockable" );
- break;
- case LockType::SharedLockable:
- TextFocused( "Type:", "shared lockable" );
- break;
- default:
- assert( false );
- break;
- }
- TextFocused( "Lock events:", RealToString( lock.timeline.size() ) );
- ImGui::Separator();
-
- const auto announce = timeAnnounce;
- const auto terminate = timeTerminate;
- const auto lifetime = timeTerminate - timeAnnounce;
- const auto traceLen = m_worker.GetLastTime();
-
- TextFocused( "Announce time:", TimeToString( announce ) );
- TextFocused( "Terminate time:", TimeToString( terminate ) );
- TextFocused( "Lifetime:", TimeToString( lifetime ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%.2f%% of trace time)", lifetime / double( traceLen ) * 100 );
- ImGui::Separator();
-
- TextFocused( "Lock hold time:", TimeToString( holdTotalTime ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%.2f%% of lock lifetime)", holdTotalTime / float( lifetime ) * 100.f );
- TextFocused( "Lock wait time:", TimeToString( waitTotalTime ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%.2f%% of lock lifetime)", waitTotalTime / float( lifetime ) * 100.f );
- TextFocused( "Max waiting threads:", RealToString( maxWaitingThreads ) );
- ImGui::Separator();
-
- const auto threadList = ImGui::TreeNode( "Thread list" );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%zu)", lock.threadList.size() );
- if( threadList )
- {
- for( const auto& t : lock.threadList )
- {
- SmallColorBox( GetThreadColor( t, 0 ) );
- ImGui::SameLine();
- ImGui::TextUnformatted( m_worker.GetThreadName( t ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", RealToString( t ) );
- }
- ImGui::TreePop();
- }
- }
- ImGui::End();
- if( !visible ) m_lockInfoWindow = InvalidId;
-}
-
void View::DrawRanges()
{
ImGui::Begin( "Time range limits", &m_showRanges, ImGuiWindowFlags_AlwaysAutoResize );
diff --git a/server/TracyView_Locks.cpp b/server/TracyView_Locks.cpp
new file mode 100644
index 00000000..0a9171d7
--- /dev/null
+++ b/server/TracyView_Locks.cpp
@@ -0,0 +1,1055 @@
+#include
+
+#include "TracyFilesystem.hpp"
+#include "TracyMouse.hpp"
+#include "TracyPrint.hpp"
+#include "TracyView.hpp"
+
+namespace tracy
+{
+
+enum { MinVisSize = 3 };
+
+static tracy_force_inline uint64_t GetThreadBit( uint8_t thread )
+{
+ return uint64_t( 1 ) << thread;
+}
+
+static tracy_force_inline bool IsThreadWaiting( uint64_t bitlist, uint64_t threadBit )
+{
+ return ( bitlist & threadBit ) != 0;
+}
+
+static tracy_force_inline bool AreOtherWaiting( uint64_t bitlist, uint64_t threadBit )
+{
+ return ( bitlist & ~threadBit ) != 0;
+}
+
+enum class LockState
+{
+ Nothing,
+ HasLock, // green
+ HasBlockingLock, // yellow
+ WaitLock // red
+};
+
+static Vector::const_iterator GetNextLockEvent( const Vector::const_iterator& it, const Vector::const_iterator& end, LockState& nextState, uint64_t threadBit )
+{
+ auto next = it;
+ next++;
+
+ switch( nextState )
+ {
+ case LockState::Nothing:
+ while( next < end )
+ {
+ if( next->lockCount != 0 )
+ {
+ if( GetThreadBit( next->lockingThread ) == threadBit )
+ {
+ nextState = AreOtherWaiting( next->waitList, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
+ break;
+ }
+ else if( IsThreadWaiting( next->waitList, threadBit ) )
+ {
+ nextState = LockState::WaitLock;
+ break;
+ }
+ }
+ next++;
+ }
+ break;
+ case LockState::HasLock:
+ while( next < end )
+ {
+ if( next->lockCount == 0 )
+ {
+ nextState = LockState::Nothing;
+ break;
+ }
+ if( next->waitList != 0 )
+ {
+ if( AreOtherWaiting( next->waitList, threadBit ) )
+ {
+ nextState = LockState::HasBlockingLock;
+ }
+ break;
+ }
+ if( next->waitList != it->waitList || next->lockCount != it->lockCount )
+ {
+ break;
+ }
+ next++;
+ }
+ break;
+ case LockState::HasBlockingLock:
+ while( next < end )
+ {
+ if( next->lockCount == 0 )
+ {
+ nextState = LockState::Nothing;
+ break;
+ }
+ if( next->waitList != it->waitList || next->lockCount != it->lockCount )
+ {
+ break;
+ }
+ next++;
+ }
+ break;
+ case LockState::WaitLock:
+ while( next < end )
+ {
+ if( GetThreadBit( next->lockingThread ) == threadBit )
+ {
+ nextState = AreOtherWaiting( next->waitList, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
+ break;
+ }
+ if( next->lockingThread != it->lockingThread )
+ {
+ break;
+ }
+ if( next->lockCount == 0 )
+ {
+ break;
+ }
+ next++;
+ }
+ break;
+ default:
+ assert( false );
+ break;
+ }
+
+ return next;
+}
+
+static Vector::const_iterator GetNextLockEventShared( const Vector::const_iterator& it, const Vector::const_iterator& end, LockState& nextState, uint64_t threadBit )
+{
+ const auto itptr = (const LockEventShared*)(const LockEvent*)it->ptr;
+ auto next = it;
+ next++;
+
+ switch( nextState )
+ {
+ case LockState::Nothing:
+ while( next < end )
+ {
+ const auto ptr = (const LockEventShared*)(const LockEvent*)next->ptr;
+ if( next->lockCount != 0 )
+ {
+ const auto wait = next->waitList | ptr->waitShared;
+ if( GetThreadBit( next->lockingThread ) == threadBit )
+ {
+ nextState = AreOtherWaiting( wait, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
+ break;
+ }
+ else if( IsThreadWaiting( wait, threadBit ) )
+ {
+ nextState = LockState::WaitLock;
+ break;
+ }
+ }
+ else if( IsThreadWaiting( ptr->sharedList, threadBit ) )
+ {
+ nextState = ( next->waitList != 0 ) ? LockState::HasBlockingLock : LockState::HasLock;
+ break;
+ }
+ else if( ptr->sharedList != 0 && IsThreadWaiting( next->waitList, threadBit ) )
+ {
+ nextState = LockState::WaitLock;
+ break;
+ }
+ next++;
+ }
+ break;
+ case LockState::HasLock:
+ while( next < end )
+ {
+ const auto ptr = (const LockEventShared*)(const LockEvent*)next->ptr;
+ if( next->lockCount == 0 && !IsThreadWaiting( ptr->sharedList, threadBit ) )
+ {
+ nextState = LockState::Nothing;
+ break;
+ }
+ if( next->waitList != 0 )
+ {
+ if( AreOtherWaiting( next->waitList, threadBit ) )
+ {
+ nextState = LockState::HasBlockingLock;
+ }
+ break;
+ }
+ else if( !IsThreadWaiting( ptr->sharedList, threadBit ) && ptr->waitShared != 0 )
+ {
+ nextState = LockState::HasBlockingLock;
+ break;
+ }
+ if( next->waitList != it->waitList || ptr->waitShared != itptr->waitShared || next->lockCount != it->lockCount || ptr->sharedList != itptr->sharedList )
+ {
+ break;
+ }
+ next++;
+ }
+ break;
+ case LockState::HasBlockingLock:
+ while( next < end )
+ {
+ const auto ptr = (const LockEventShared*)(const LockEvent*)next->ptr;
+ if( next->lockCount == 0 && !IsThreadWaiting( ptr->sharedList, threadBit ) )
+ {
+ nextState = LockState::Nothing;
+ break;
+ }
+ if( next->waitList != it->waitList || ptr->waitShared != itptr->waitShared || next->lockCount != it->lockCount || ptr->sharedList != itptr->sharedList )
+ {
+ break;
+ }
+ next++;
+ }
+ break;
+ case LockState::WaitLock:
+ while( next < end )
+ {
+ const auto ptr = (const LockEventShared*)(const LockEvent*)next->ptr;
+ if( GetThreadBit( next->lockingThread ) == threadBit )
+ {
+ const auto wait = next->waitList | ptr->waitShared;
+ nextState = AreOtherWaiting( wait, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
+ break;
+ }
+ if( IsThreadWaiting( ptr->sharedList, threadBit ) )
+ {
+ nextState = ( next->waitList != 0 ) ? LockState::HasBlockingLock : LockState::HasLock;
+ break;
+ }
+ if( next->lockingThread != it->lockingThread )
+ {
+ break;
+ }
+ if( next->lockCount == 0 && !IsThreadWaiting( ptr->waitShared, threadBit ) )
+ {
+ break;
+ }
+ next++;
+ }
+ break;
+ default:
+ assert( false );
+ break;
+ }
+
+ return next;
+}
+
+static LockState CombineLockState( LockState state, LockState next )
+{
+ return (LockState)std::max( (int)state, (int)next );
+}
+
+void View::DrawLockHeader( uint32_t id, const LockMap& lockmap, const SourceLocation& srcloc, bool hover, ImDrawList* draw, const ImVec2& wpos, float w, float ty, float offset, uint8_t tid )
+{
+ char buf[1024];
+ if( lockmap.customName.Active() )
+ {
+ sprintf( buf, "%" PRIu32 ": %s", id, m_worker.GetString( lockmap.customName ) );
+ }
+ else
+ {
+ sprintf( buf, "%" PRIu32 ": %s", id, m_worker.GetString( srcloc.function ) );
+ }
+ DrawTextContrast( draw, wpos + ImVec2( 0, offset ), 0xFF8888FF, buf );
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty + 1 ) ) )
+ {
+ m_lockHoverHighlight = id;
+
+ if( ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( ty + ImGui::CalcTextSize( buf ).x, offset + ty + 1 ) ) )
+ {
+ const auto& range = lockmap.range[tid];
+ const auto activity = range.end - range.start;
+ const auto traceLen = m_worker.GetLastTime();
+
+ int64_t timeAnnounce = lockmap.timeAnnounce;
+ int64_t timeTerminate = lockmap.timeTerminate;
+ if( !lockmap.timeline.empty() )
+ {
+ if( timeAnnounce <= 0 )
+ {
+ timeAnnounce = lockmap.timeline.front().ptr->Time();
+ }
+ if( timeTerminate <= 0 )
+ {
+ timeTerminate = lockmap.timeline.back().ptr->Time();
+ }
+ }
+ const auto lockLen = timeTerminate - timeAnnounce;
+
+ ImGui::BeginTooltip();
+ switch( lockmap.type )
+ {
+ case LockType::Lockable:
+ TextFocused( "Type:", "lockable" );
+ break;
+ case LockType::SharedLockable:
+ TextFocused( "Type:", "shared lockable" );
+ break;
+ default:
+ assert( false );
+ break;
+ }
+ ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
+ ImGui::Separator();
+ TextFocused( ICON_FA_RANDOM " Appeared at", TimeToString( range.start ) );
+ TextFocused( ICON_FA_RANDOM " Last event at", TimeToString( range.end ) );
+ TextFocused( ICON_FA_RANDOM " Activity time:", TimeToString( activity ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%.2f%% of lock lifetime)", activity / double( lockLen ) * 100 );
+ ImGui::Separator();
+ TextFocused( "Announce time:", TimeToString( timeAnnounce ) );
+ TextFocused( "Terminate time:", TimeToString( timeTerminate ) );
+ TextFocused( "Lifetime:", TimeToString( lockLen ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%.2f%% of trace time)", lockLen / double( traceLen ) * 100 );
+ ImGui::Separator();
+ TextDisabledUnformatted( "Thread list:" );
+ ImGui::Indent( ty );
+ for( const auto& t : lockmap.threadList )
+ {
+ SmallColorBox( GetThreadColor( t, 0 ) );
+ ImGui::SameLine();
+ ImGui::TextUnformatted( m_worker.GetThreadName( t ) );
+ }
+ ImGui::Unindent( ty );
+ ImGui::Separator();
+ TextFocused( "Lock events:", RealToString( lockmap.timeline.size() ) );
+ ImGui::EndTooltip();
+
+ if( IsMouseClicked( 0 ) )
+ {
+ m_lockInfoWindow = id;
+ }
+ if( IsMouseClicked( 2 ) )
+ {
+ ZoomToRange( range.start, range.end );
+ }
+ }
+ }
+}
+
+int View::DrawLocks( uint64_t tid, bool hover, double pxns, const ImVec2& wpos, int _offset, LockHighlight& highlight, float yMin, float yMax )
+{
+ const auto delay = m_worker.GetDelay();
+ const auto resolution = m_worker.GetResolution();
+ const auto w = ImGui::GetContentRegionAvail().x - 1;
+ const auto ty = ImGui::GetTextLineHeight();
+ const auto ostep = ty + 1;
+ auto draw = ImGui::GetWindowDrawList();
+ const auto dsz = delay * pxns;
+ const auto rsz = resolution * pxns;
+ const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
+
+ const auto ty025 = round( ty * 0.25f );
+ const auto ty05 = round( ty * 0.5f );
+ const auto ty075 = round( ty * 0.75f );
+
+ int cnt = 0;
+ for( const auto& v : m_worker.GetLockMap() )
+ {
+ const auto& lockmap = *v.second;
+ if( !lockmap.valid || !Vis( &lockmap ).visible ) continue;
+ if( m_vd.onlyContendedLocks && ( lockmap.threadList.size() == 1 || !lockmap.isContended ) && m_lockInfoWindow != v.first ) continue;
+
+ auto it = lockmap.threadMap.find( tid );
+ if( it == lockmap.threadMap.end() ) continue;
+
+ const auto offset = _offset + ostep * cnt;
+
+ const auto& range = lockmap.range[it->second];
+ const auto& tl = lockmap.timeline;
+ assert( !tl.empty() );
+ if( range.start > m_vd.zvEnd || range.end < m_vd.zvStart )
+ {
+ if( m_lockInfoWindow == v.first )
+ {
+ draw->AddRectFilled( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x2288DD88 );
+ draw->AddRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x4488DD88 );
+ DrawLockHeader( v.first, lockmap, m_worker.GetSourceLocation( lockmap.srcloc ), hover, draw, wpos, w, ty, offset, it->second );
+ cnt++;
+ }
+
+ continue;
+ }
+
+ auto GetNextLockFunc = lockmap.type == LockType::Lockable ? GetNextLockEvent : GetNextLockEventShared;
+
+ const auto thread = it->second;
+ const auto threadBit = GetThreadBit( thread );
+
+ auto vbegin = std::lower_bound( tl.begin(), tl.end(), std::max( range.start, m_vd.zvStart - delay ), [] ( const auto& l, const auto& r ) { return l.ptr->Time() < r; } );
+ const auto vend = std::lower_bound( vbegin, tl.end(), std::min( range.end, m_vd.zvEnd + resolution ), [] ( const auto& l, const auto& r ) { return l.ptr->Time() < r; } );
+
+ if( vbegin > tl.begin() ) vbegin--;
+
+ LockState state = LockState::Nothing;
+ if( lockmap.type == LockType::Lockable )
+ {
+ if( vbegin->lockCount != 0 )
+ {
+ if( vbegin->lockingThread == thread )
+ {
+ state = AreOtherWaiting( vbegin->waitList, threadBit ) ? LockState::HasBlockingLock : LockState::HasLock;
+ }
+ else if( IsThreadWaiting( vbegin->waitList, threadBit ) )
+ {
+ state = LockState::WaitLock;
+ }
+ }
+ }
+ else
+ {
+ auto ptr = (const LockEventShared*)(const LockEvent*)vbegin->ptr;
+ if( vbegin->lockCount != 0 )
+ {
+ if( vbegin->lockingThread == thread )
+ {
+ state = ( AreOtherWaiting( vbegin->waitList, threadBit ) || AreOtherWaiting( ptr->waitShared, threadBit ) ) ? LockState::HasBlockingLock : LockState::HasLock;
+ }
+ else if( IsThreadWaiting( vbegin->waitList, threadBit ) || IsThreadWaiting( ptr->waitShared, threadBit ) )
+ {
+ state = LockState::WaitLock;
+ }
+ }
+ else if( IsThreadWaiting( ptr->sharedList, threadBit ) )
+ {
+ state = vbegin->waitList != 0 ? LockState::HasBlockingLock : LockState::HasLock;
+ }
+ else if( ptr->sharedList != 0 && IsThreadWaiting( vbegin->waitList, threadBit ) )
+ {
+ state = LockState::WaitLock;
+ }
+ }
+
+ const auto yPos = wpos.y + offset;
+ if( yPos + ostep >= yMin && yPos <= yMax )
+ {
+ bool drawn = false;
+ const auto& srcloc = m_worker.GetSourceLocation( lockmap.srcloc );
+
+ double pxend = 0;
+ for(;;)
+ {
+ if( m_vd.onlyContendedLocks )
+ {
+ while( vbegin < vend && ( state == LockState::Nothing || state == LockState::HasLock ) )
+ {
+ vbegin = GetNextLockFunc( vbegin, vend, state, threadBit );
+ }
+ }
+ else
+ {
+ while( vbegin < vend && state == LockState::Nothing )
+ {
+ vbegin = GetNextLockFunc( vbegin, vend, state, threadBit );
+ }
+ }
+ if( vbegin >= vend ) break;
+
+ assert( state != LockState::Nothing && ( !m_vd.onlyContendedLocks || state != LockState::HasLock ) );
+ drawn = true;
+
+ LockState drawState = state;
+ auto next = GetNextLockFunc( vbegin, vend, state, threadBit );
+
+ const auto t0 = vbegin->ptr->Time();
+ int64_t t1 = next == tl.end() ? m_worker.GetLastTime() : next->ptr->Time();
+ const auto px0 = std::max( pxend, ( t0 - m_vd.zvStart ) * pxns );
+ auto tx0 = px0;
+ double px1 = ( t1 - m_vd.zvStart ) * pxns;
+ uint64_t condensed = 0;
+
+ if( m_vd.onlyContendedLocks )
+ {
+ for(;;)
+ {
+ if( next >= vend || px1 - tx0 > MinVisSize ) break;
+ auto n = next;
+ auto ns = state;
+ while( n < vend && ( ns == LockState::Nothing || ns == LockState::HasLock ) )
+ {
+ n = GetNextLockFunc( n, vend, ns, threadBit );
+ }
+ if( n >= vend ) break;
+ if( n == next )
+ {
+ n = GetNextLockFunc( n, vend, ns, threadBit );
+ }
+ drawState = CombineLockState( drawState, state );
+ condensed++;
+ const auto t2 = n == tl.end() ? m_worker.GetLastTime() : n->ptr->Time();
+ const auto px2 = ( t2 - m_vd.zvStart ) * pxns;
+ if( px2 - px1 > MinVisSize ) break;
+ if( drawState != ns && px2 - px0 > MinVisSize && !( ns == LockState::Nothing || ns == LockState::HasLock ) ) break;
+ t1 = t2;
+ tx0 = px1;
+ px1 = px2;
+ next = n;
+ state = ns;
+ }
+ }
+ else
+ {
+ for(;;)
+ {
+ if( next >= vend || px1 - tx0 > MinVisSize ) break;
+ auto n = next;
+ auto ns = state;
+ while( n < vend && ns == LockState::Nothing )
+ {
+ n = GetNextLockFunc( n, vend, ns, threadBit );
+ }
+ if( n >= vend ) break;
+ if( n == next )
+ {
+ n = GetNextLockFunc( n, vend, ns, threadBit );
+ }
+ drawState = CombineLockState( drawState, state );
+ condensed++;
+ const auto t2 = n == tl.end() ? m_worker.GetLastTime() : n->ptr->Time();
+ const auto px2 = ( t2 - m_vd.zvStart ) * pxns;
+ if( px2 - px1 > MinVisSize ) break;
+ if( drawState != ns && px2 - px0 > MinVisSize && ns != LockState::Nothing ) break;
+ t1 = t2;
+ tx0 = px1;
+ px1 = px2;
+ next = n;
+ state = ns;
+ }
+ }
+
+ pxend = std::max( { px1, px0+MinVisSize, px0 + pxns * 0.5 } );
+
+ bool itemHovered = hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( pxend, double( w + 10 ) ), offset + ty + 1 ) );
+ if( itemHovered )
+ {
+ if( IsMouseClicked( 0 ) )
+ {
+ m_lockInfoWindow = v.first;
+ }
+ if( IsMouseClicked( 2 ) )
+ {
+ ZoomToRange( t0, t1 );
+ }
+
+ if( condensed > 1 )
+ {
+ ImGui::BeginTooltip();
+ TextFocused( "Multiple lock events:", RealToString( condensed ) );
+ ImGui::EndTooltip();
+ }
+ else
+ {
+ highlight.blocked = drawState == LockState::HasBlockingLock;
+ if( !highlight.blocked )
+ {
+ highlight.id = v.first;
+ highlight.begin = t0;
+ highlight.end = t1;
+ highlight.thread = thread;
+ highlight.blocked = false;
+ }
+ else
+ {
+ auto b = vbegin;
+ while( b != tl.begin() )
+ {
+ if( b->lockingThread != vbegin->lockingThread )
+ {
+ break;
+ }
+ b--;
+ }
+ b++;
+ highlight.begin = b->ptr->Time();
+
+ auto e = next;
+ while( e != tl.end() )
+ {
+ if( e->lockingThread != next->lockingThread )
+ {
+ highlight.id = v.first;
+ highlight.end = e->ptr->Time();
+ highlight.thread = thread;
+ break;
+ }
+ e++;
+ }
+ }
+
+ ImGui::BeginTooltip();
+ if( v.second->customName.Active() )
+ {
+ ImGui::Text( "Lock #%" PRIu32 ": %s", v.first, m_worker.GetString( v.second->customName ) );
+ }
+ else
+ {
+ ImGui::Text( "Lock #%" PRIu32 ": %s", v.first, m_worker.GetString( srcloc.function ) );
+ }
+ ImGui::Separator();
+ ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
+ TextFocused( "Time:", TimeToString( t1 - t0 ) );
+ ImGui::Separator();
+
+ int16_t markloc = 0;
+ auto it = vbegin;
+ for(;;)
+ {
+ if( it->ptr->thread == thread )
+ {
+ if( ( it->lockingThread == thread || IsThreadWaiting( it->waitList, threadBit ) ) && it->ptr->SrcLoc() != 0 )
+ {
+ markloc = it->ptr->SrcLoc();
+ break;
+ }
+ }
+ if( it == tl.begin() ) break;
+ --it;
+ }
+ if( markloc != 0 )
+ {
+ const auto& marklocdata = m_worker.GetSourceLocation( markloc );
+ ImGui::TextUnformatted( "Lock event location:" );
+ ImGui::TextUnformatted( m_worker.GetString( marklocdata.function ) );
+ ImGui::TextUnformatted( LocationToString( m_worker.GetString( marklocdata.file ), marklocdata.line ) );
+ ImGui::Separator();
+ }
+
+ if( lockmap.type == LockType::Lockable )
+ {
+ switch( drawState )
+ {
+ case LockState::HasLock:
+ if( vbegin->lockCount == 1 )
+ {
+ ImGui::Text( "Thread \"%s\" has lock. No other threads are waiting.", m_worker.GetThreadName( tid ) );
+ }
+ else
+ {
+ ImGui::Text( "Thread \"%s\" has %i locks. No other threads are waiting.", m_worker.GetThreadName( tid ), vbegin->lockCount );
+ }
+ if( vbegin->waitList != 0 )
+ {
+ assert( !AreOtherWaiting( next->waitList, threadBit ) );
+ ImGui::TextUnformatted( "Recursive lock acquire in thread." );
+ }
+ break;
+ case LockState::HasBlockingLock:
+ {
+ if( vbegin->lockCount == 1 )
+ {
+ ImGui::Text( "Thread \"%s\" has lock. Blocked threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), TracyCountBits( vbegin->waitList ) );
+ }
+ else
+ {
+ ImGui::Text( "Thread \"%s\" has %i locks. Blocked threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), vbegin->lockCount, TracyCountBits( vbegin->waitList ) );
+ }
+ auto waitList = vbegin->waitList;
+ int t = 0;
+ ImGui::Indent( ty );
+ while( waitList != 0 )
+ {
+ if( waitList & 0x1 )
+ {
+ ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
+ }
+ waitList >>= 1;
+ t++;
+ }
+ ImGui::Unindent( ty );
+ break;
+ }
+ case LockState::WaitLock:
+ {
+ if( vbegin->lockCount > 0 )
+ {
+ ImGui::Text( "Thread \"%s\" is blocked by other thread:", m_worker.GetThreadName( tid ) );
+ }
+ else
+ {
+ ImGui::Text( "Thread \"%s\" waits to obtain lock after release by thread:", m_worker.GetThreadName( tid ) );
+ }
+ ImGui::Indent( ty );
+ ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[vbegin->lockingThread] ) );
+ ImGui::Unindent( ty );
+ break;
+ }
+ default:
+ assert( false );
+ break;
+ }
+ }
+ else
+ {
+ const auto ptr = (const LockEventShared*)(const LockEvent*)vbegin->ptr;
+ switch( drawState )
+ {
+ case LockState::HasLock:
+ assert( vbegin->waitList == 0 );
+ if( ptr->sharedList == 0 )
+ {
+ assert( vbegin->lockCount == 1 );
+ ImGui::Text( "Thread \"%s\" has lock. No other threads are waiting.", m_worker.GetThreadName( tid ) );
+ }
+ else if( TracyCountBits( ptr->sharedList ) == 1 )
+ {
+ ImGui::Text( "Thread \"%s\" has a sole shared lock. No other threads are waiting.", m_worker.GetThreadName( tid ) );
+ }
+ else
+ {
+ ImGui::Text( "Thread \"%s\" has shared lock. No other threads are waiting.", m_worker.GetThreadName( tid ) );
+ ImGui::Text( "Threads sharing the lock (%" PRIu64 "):", TracyCountBits( ptr->sharedList ) - 1 );
+ auto sharedList = ptr->sharedList;
+ int t = 0;
+ ImGui::Indent( ty );
+ while( sharedList != 0 )
+ {
+ if( sharedList & 0x1 && t != thread )
+ {
+ ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
+ }
+ sharedList >>= 1;
+ t++;
+ }
+ ImGui::Unindent( ty );
+ }
+ break;
+ case LockState::HasBlockingLock:
+ {
+ if( ptr->sharedList == 0 )
+ {
+ assert( vbegin->lockCount == 1 );
+ ImGui::Text( "Thread \"%s\" has lock. Blocked threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), TracyCountBits( vbegin->waitList ) + TracyCountBits( ptr->waitShared ) );
+ }
+ else if( TracyCountBits( ptr->sharedList ) == 1 )
+ {
+ ImGui::Text( "Thread \"%s\" has a sole shared lock. Blocked threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), TracyCountBits( vbegin->waitList ) + TracyCountBits( ptr->waitShared ) );
+ }
+ else
+ {
+ ImGui::Text( "Thread \"%s\" has shared lock.", m_worker.GetThreadName( tid ) );
+ ImGui::Text( "Threads sharing the lock (%" PRIu64 "):", TracyCountBits( ptr->sharedList ) - 1 );
+ auto sharedList = ptr->sharedList;
+ int t = 0;
+ ImGui::Indent( ty );
+ while( sharedList != 0 )
+ {
+ if( sharedList & 0x1 && t != thread )
+ {
+ ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
+ }
+ sharedList >>= 1;
+ t++;
+ }
+ ImGui::Unindent( ty );
+ ImGui::Text( "Blocked threads (%" PRIu64 "):", TracyCountBits( vbegin->waitList ) + TracyCountBits( ptr->waitShared ) );
+ }
+
+ auto waitList = vbegin->waitList;
+ int t = 0;
+ ImGui::Indent( ty );
+ while( waitList != 0 )
+ {
+ if( waitList & 0x1 )
+ {
+ ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
+ }
+ waitList >>= 1;
+ t++;
+ }
+ auto waitShared = ptr->waitShared;
+ t = 0;
+ while( waitShared != 0 )
+ {
+ if( waitShared & 0x1 )
+ {
+ ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
+ }
+ waitShared >>= 1;
+ t++;
+ }
+ ImGui::Unindent( ty );
+ break;
+ }
+ case LockState::WaitLock:
+ {
+ assert( vbegin->lockCount == 0 || vbegin->lockCount == 1 );
+ if( vbegin->lockCount != 0 || ptr->sharedList != 0 )
+ {
+ ImGui::Text( "Thread \"%s\" is blocked by other threads (%" PRIu64 "):", m_worker.GetThreadName( tid ), vbegin->lockCount + TracyCountBits( ptr->sharedList ) );
+ }
+ else
+ {
+ ImGui::Text( "Thread \"%s\" waits to obtain lock after release by thread:", m_worker.GetThreadName( tid ) );
+ }
+ ImGui::Indent( ty );
+ if( vbegin->lockCount != 0 )
+ {
+ ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[vbegin->lockingThread] ) );
+ }
+ auto sharedList = ptr->sharedList;
+ int t = 0;
+ while( sharedList != 0 )
+ {
+ if( sharedList & 0x1 )
+ {
+ ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threadList[t] ) );
+ }
+ sharedList >>= 1;
+ t++;
+ }
+ ImGui::Unindent( ty );
+ break;
+ }
+ default:
+ assert( false );
+ break;
+ }
+ }
+ ImGui::EndTooltip();
+ }
+ }
+
+ const auto cfilled = drawState == LockState::HasLock ? 0xFF228A22 : ( drawState == LockState::HasBlockingLock ? 0xFF228A8A : 0xFF2222BD );
+ draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( pxend, double( w + 10 ) ), offset + ty ), cfilled );
+ if( m_lockHighlight.thread != thread && ( drawState == LockState::HasBlockingLock ) != m_lockHighlight.blocked && next != tl.end() && m_lockHighlight.id == int64_t( v.first ) && m_lockHighlight.begin <= vbegin->ptr->Time() && m_lockHighlight.end >= next->ptr->Time() )
+ {
+ const auto t = uint8_t( ( sin( std::chrono::duration_cast( std::chrono::system_clock::now().time_since_epoch() ).count() * 0.01 ) * 0.5 + 0.5 ) * 255 );
+ draw->AddRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( pxend, double( w + 10 ) ), offset + ty ), 0x00FFFFFF | ( t << 24 ), 0.f, -1, 2.f );
+ }
+ else if( condensed == 0 )
+ {
+ const auto coutline = drawState == LockState::HasLock ? 0xFF3BA33B : ( drawState == LockState::HasBlockingLock ? 0xFF3BA3A3 : 0xFF3B3BD6 );
+ draw->AddRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( pxend, double( w + 10 ) ), offset + ty ), coutline );
+ }
+ else if( condensed > 1 )
+ {
+ DrawZigZag( draw, wpos + ImVec2( 0, offset + ty05 ), px0, pxend, ty025, DarkenColor( cfilled ) );
+ }
+
+ const auto rx0 = ( t0 - m_vd.zvStart ) * pxns;
+ if( dsz >= MinVisSize )
+ {
+ draw->AddRectFilled( wpos + ImVec2( rx0, offset ), wpos + ImVec2( std::min( rx0+dsz, px1 ), offset + ty ), 0x882222DD );
+ }
+ if( rsz >= MinVisSize )
+ {
+ DrawLine( draw, dpos + ImVec2( rx0 + rsz, offset + ty05 ), dpos + ImVec2( rx0 - rsz, offset + ty05 ), 0xAAFFFFFF );
+ DrawLine( draw, dpos + ImVec2( rx0 + rsz, offset + ty025 ), dpos + ImVec2( rx0 + rsz, offset + ty075 ), 0xAAFFFFFF );
+ DrawLine( draw, dpos + ImVec2( rx0 - rsz, offset + ty025 ), dpos + ImVec2( rx0 - rsz, offset + ty075 ), 0xAAFFFFFF );
+
+ DrawLine( draw, dpos + ImVec2( px1 + rsz, offset + ty05 ), dpos + ImVec2( px1 - rsz, offset + ty05 ), 0xAAFFFFFF );
+ DrawLine( draw, dpos + ImVec2( px1 + rsz, offset + ty025 ), dpos + ImVec2( px1 + rsz, offset + ty075 ), 0xAAFFFFFF );
+ DrawLine( draw, dpos + ImVec2( px1 - rsz, offset + ty025 ), dpos + ImVec2( px1 - rsz, offset + ty075 ), 0xAAFFFFFF );
+ }
+
+ vbegin = next;
+ }
+
+ if( drawn || m_lockInfoWindow == v.first )
+ {
+ if( m_lockInfoWindow == v.first )
+ {
+ draw->AddRectFilled( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x2288DD88 );
+ draw->AddRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x4488DD88 );
+ }
+ else if( m_lockHoverHighlight == v.first )
+ {
+ draw->AddRectFilled( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x228888DD );
+ draw->AddRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + ty ), 0x448888DD );
+ }
+
+ DrawLockHeader( v.first, lockmap, srcloc, hover, draw, wpos, w, ty, offset, it->second );
+ cnt++;
+ }
+ }
+ else
+ {
+ while( vbegin < vend && ( state == LockState::Nothing || ( m_vd.onlyContendedLocks && state == LockState::HasLock ) ) )
+ {
+ vbegin = GetNextLockFunc( vbegin, vend, state, threadBit );
+ }
+ if( vbegin < vend ) cnt++;
+ }
+ }
+ return cnt;
+}
+
+void View::DrawLockInfoWindow()
+{
+ bool visible = true;
+ ImGui::Begin( "Lock info", &visible, ImGuiWindowFlags_AlwaysAutoResize );
+ if( !ImGui::GetCurrentWindowRead()->SkipItems )
+ {
+ auto it = m_worker.GetLockMap().find( m_lockInfoWindow );
+ assert( it != m_worker.GetLockMap().end() );
+ const auto& lock = *it->second;
+ const auto& srcloc = m_worker.GetSourceLocation( lock.srcloc );
+ auto fileName = m_worker.GetString( srcloc.file );
+
+ int64_t timeAnnounce = lock.timeAnnounce;
+ int64_t timeTerminate = lock.timeTerminate;
+ if( !lock.timeline.empty() )
+ {
+ if( timeAnnounce <= 0 )
+ {
+ timeAnnounce = lock.timeline.front().ptr->Time();
+ }
+ if( timeTerminate <= 0 )
+ {
+ timeTerminate = lock.timeline.back().ptr->Time();
+ }
+ }
+
+ bool waitState = false;
+ bool holdState = false;
+ int64_t waitStartTime = 0;
+ int64_t holdStartTime = 0;
+ int64_t waitTotalTime = 0;
+ int64_t holdTotalTime = 0;
+ uint32_t maxWaitingThreads = 0;
+ for( auto& v : lock.timeline )
+ {
+ if( holdState )
+ {
+ if( v.lockCount == 0 )
+ {
+ holdTotalTime += v.ptr->Time() - holdStartTime;
+ holdState = false;
+ }
+ }
+ else
+ {
+ if( v.lockCount != 0 )
+ {
+ holdStartTime = v.ptr->Time();
+ holdState = true;
+ }
+ }
+ if( waitState )
+ {
+ if( v.waitList == 0 )
+ {
+ waitTotalTime += v.ptr->Time() - waitStartTime;
+ waitState = false;
+ }
+ else
+ {
+ maxWaitingThreads = std::max( maxWaitingThreads, TracyCountBits( v.waitList ) );
+ }
+ }
+ else
+ {
+ if( v.waitList != 0 )
+ {
+ waitStartTime = v.ptr->Time();
+ waitState = true;
+ maxWaitingThreads = std::max( maxWaitingThreads, TracyCountBits( v.waitList ) );
+ }
+ }
+ }
+
+ ImGui::PushFont( m_bigFont );
+ if( lock.customName.Active() )
+ {
+ ImGui::Text( "Lock #%" PRIu32 ": %s", m_lockInfoWindow, m_worker.GetString( lock.customName ) );
+ }
+ else
+ {
+ ImGui::Text( "Lock #%" PRIu32 ": %s", m_lockInfoWindow, m_worker.GetString( srcloc.function ) );
+ }
+ ImGui::PopFont();
+ if( lock.customName.Active() )
+ {
+ TextFocused( "Name:", m_worker.GetString( srcloc.function ) );
+ }
+ TextDisabledUnformatted( "Location:" );
+ if( m_lockInfoAnim.Match( m_lockInfoWindow ) )
+ {
+ const auto time = m_lockInfoAnim.Time();
+ const auto indentVal = sin( time * 60.f ) * 10.f * time;
+ ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
+ }
+ else
+ {
+ ImGui::SameLine();
+ }
+ ImGui::TextUnformatted( LocationToString( fileName, srcloc.line ) );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
+ {
+ ViewSource( fileName, srcloc.line );
+ }
+ else
+ {
+ m_lockInfoAnim.Enable( m_lockInfoWindow, 0.5f );
+ }
+ }
+ ImGui::Separator();
+
+ switch( lock.type )
+ {
+ case LockType::Lockable:
+ TextFocused( "Type:", "lockable" );
+ break;
+ case LockType::SharedLockable:
+ TextFocused( "Type:", "shared lockable" );
+ break;
+ default:
+ assert( false );
+ break;
+ }
+ TextFocused( "Lock events:", RealToString( lock.timeline.size() ) );
+ ImGui::Separator();
+
+ const auto announce = timeAnnounce;
+ const auto terminate = timeTerminate;
+ const auto lifetime = timeTerminate - timeAnnounce;
+ const auto traceLen = m_worker.GetLastTime();
+
+ TextFocused( "Announce time:", TimeToString( announce ) );
+ TextFocused( "Terminate time:", TimeToString( terminate ) );
+ TextFocused( "Lifetime:", TimeToString( lifetime ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%.2f%% of trace time)", lifetime / double( traceLen ) * 100 );
+ ImGui::Separator();
+
+ TextFocused( "Lock hold time:", TimeToString( holdTotalTime ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%.2f%% of lock lifetime)", holdTotalTime / float( lifetime ) * 100.f );
+ TextFocused( "Lock wait time:", TimeToString( waitTotalTime ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%.2f%% of lock lifetime)", waitTotalTime / float( lifetime ) * 100.f );
+ TextFocused( "Max waiting threads:", RealToString( maxWaitingThreads ) );
+ ImGui::Separator();
+
+ const auto threadList = ImGui::TreeNode( "Thread list" );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%zu)", lock.threadList.size() );
+ if( threadList )
+ {
+ for( const auto& t : lock.threadList )
+ {
+ SmallColorBox( GetThreadColor( t, 0 ) );
+ ImGui::SameLine();
+ ImGui::TextUnformatted( m_worker.GetThreadName( t ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", RealToString( t ) );
+ }
+ ImGui::TreePop();
+ }
+ }
+ ImGui::End();
+ if( !visible ) m_lockInfoWindow = InvalidId;
+}
+
+}