2022-07-02 13:24:09 +00:00
# include <inttypes.h>
# include "TracyFilesystem.hpp"
2022-07-02 15:00:08 +00:00
# include "TracyImGui.hpp"
2022-07-02 13:24:09 +00:00
# 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 < LockEventPtr > : : const_iterator GetNextLockEvent ( const Vector < LockEventPtr > : : const_iterator & it , const Vector < LockEventPtr > : : 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 < LockEventPtr > : : const_iterator GetNextLockEventShared ( const Vector < LockEventPtr > : : const_iterator & it , const Vector < LockEventPtr > : : 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 : : milliseconds > ( 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 < uint32_t > ( maxWaitingThreads , TracyCountBits ( v . waitList ) ) ;
}
}
else
{
if ( v . waitList ! = 0 )
{
waitStartTime = v . ptr - > Time ( ) ;
waitState = true ;
maxWaitingThreads = std : : max < uint32_t > ( 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 ;
}
}