Mark deadlocked threads and locks on the timeline.

This commit is contained in:
Bartosz Taudul
2026-09-22 02:21:57 +02:00
parent 7a975bea44
commit 7a8e9bb727
3 changed files with 60 additions and 4 deletions

View File

@@ -749,7 +749,7 @@ void TimelineItemThread::PreprocessLocks( const TimelineContext& ctx, const unor
const auto& lockmap = *v.second;
if( !lockmap.valid ) continue;
if( !m_view.Vis( &lockmap ) ) continue;
if( vd.onlyContendedLocks && lockInfoWindow != v.first && ( lockmap.threads.size() == 1 || !lockmap.isContended ) ) continue;
if( vd.onlyContendedLocks && lockInfoWindow != v.first && ( lockmap.threads.size() == 1 || !lockmap.isContended ) && !m_worker.IsDeadlockedPair( tid, v.first ) ) continue;
auto it = lockmap.threadMap.find( tid );
if( it == lockmap.threadMap.end() ) continue;

View File

@@ -186,6 +186,7 @@ int View::DrawLocks( const TimelineContext& ctx, const std::vector<std::unique_p
// The usual method of collapsing single small zones into zig-zags would be very bad here. Lock wait zones should
// be easily visible without having to zoom in first. This sets a minimum width for any lock zone.
const auto px1 = std::max( ( t1 - vStart ) * pxns, px0 + MinVisPx );
const bool deadlockWait = v.seg->nextEv == LockEvent::NoEvent && ( v.seg->flags & ( LockEventFlags::Waiting | LockEventFlags::SharedWaiting ) ) != 0 && m_worker.IsDeadlockedPair( tid, lock.id );
bool itemHovered = hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + ostep ) );
if( itemHovered )
@@ -338,6 +339,10 @@ int View::DrawLocks( const TimelineContext& ctx, const std::vector<std::unique_p
ImGui::Indent( ty );
ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threads[hinfo.holder].thread ) );
ImGui::Unindent( ty );
if( deadlockWait )
{
DrawDeadlockDetail( tid, lock.id );
}
break;
}
default:
@@ -411,6 +416,10 @@ int View::DrawLocks( const TimelineContext& ctx, const std::vector<std::unique_p
ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threads[s].thread ) );
} );
ImGui::Unindent( ty );
if( deadlockWait )
{
DrawDeadlockDetail( tid, lock.id );
}
break;
}
case LockEventState::WaitLock:
@@ -433,6 +442,10 @@ int View::DrawLocks( const TimelineContext& ctx, const std::vector<std::unique_p
ImGui::Text( "\"%s\"", m_worker.GetThreadName( lockmap.threads[s].thread ) );
} );
ImGui::Unindent( ty );
if( deadlockWait )
{
DrawDeadlockDetail( tid, lock.id );
}
break;
}
default:
@@ -444,8 +457,14 @@ int View::DrawLocks( const TimelineContext& ctx, const std::vector<std::unique_p
}
}
const auto cfilled = v.state == LockEventState::HasLock ? 0xFF228A22 : ( v.state == LockEventState::HasBlockingLock ? 0xFF228A8A : 0xFF2222BD );
const auto cfilled = deadlockWait ? 0xFF2222BD : ( v.state == LockEventState::HasLock ? 0xFF228A22 : ( v.state == LockEventState::HasBlockingLock ? 0xFF228A8A : 0xFF2222BD ) );
draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( px1, double( w + 10 ) ), offset + ty ), cfilled );
if( deadlockWait )
{
ImGui::PushFont( g_fonts.normal, FontSmall );
DrawTextContrast( draw, wpos + ImVec2( std::max( px0, -10.0 ), offset ), 0xFF2222FF, ICON_FA_ARROWS_SPIN );
ImGui::PopFont();
}
if( m_lockHighlight.thread != lock.thread && ( v.state == LockEventState::HasBlockingLock ) != m_lockHighlight.blocked && v.seg->nextEv != LockEvent::NoEvent && m_lockHighlight.id == int64_t( lock.id ) && m_lockHighlight.begin <= t1 && m_lockHighlight.end >= t0 )
{
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 );
@@ -454,7 +473,7 @@ int View::DrawLocks( const TimelineContext& ctx, const std::vector<std::unique_p
}
else if( v.num == 1 )
{
const auto coutline = v.state == LockEventState::HasLock ? 0xFF3BA33B : ( v.state == LockEventState::HasBlockingLock ? 0xFF3BA3A3 : 0xFF3B3BD6 );
const auto coutline = deadlockWait ? 0xFF3B3BD6 : ( v.state == LockEventState::HasLock ? 0xFF3BA33B : ( v.state == LockEventState::HasBlockingLock ? 0xFF3BA3A3 : 0xFF3B3BD6 ) );
draw->AddRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( px1, double( w + 10 ) ), offset + ty ), coutline );
}
else
@@ -511,6 +530,7 @@ void View::DrawLockInfoWindow()
ImGui::PushFont( g_fonts.normal, FontBig );
ImGui::Text( "Lock #%" PRIu32 ": %s", m_lockInfoWindow, GetLockDisplayName( m_worker, lock ) );
ImGui::PopFont();
if( lock.customName.Active() )
{
TextFocused( "Name:", m_worker.GetString( srcloc.function ) );
@@ -578,8 +598,10 @@ void View::DrawLockInfoWindow()
ImGui::SameLine();
ImGui::TextDisabled( "(%.2f%% of lock lifetime)", lock.waitTotalAgg / float( lifetime ) * 100.f );
TextFocused( "Max waiting threads:", RealToString( lock.maxWaiting ) );
ImGui::Separator();
DrawDeadlockDetail( AnyThread, m_lockInfoWindow );
ImGui::Separator();
const auto threadList = ImGui::TreeNode( "Thread list" );
ImGui::SameLine();
ImGui::TextDisabled( "(%zu)", lock.threads.size() );

View File

@@ -201,6 +201,40 @@ void View::DrawThreadMessagesList( const TimelineContext& ctx, const std::vector
}
}
}
{
const auto& dgroups = m_worker.GetDeadlockGroups();
const auto& dmembers = m_worker.GetDeadlockMembers();
for( const auto& dlk : dgroups )
{
for( uint32_t mi=dlk.first; mi<dlk.first+dlk.cnt; mi++ )
{
const auto& m = dmembers[mi];
if( m.thread != tid || m.waitTime < vStart || m.waitTime > vEnd ) continue;
const auto px = ( m.waitTime - vStart ) * pxns;
draw->AddTriangleFilled( wpos + ImVec2( px - (ty - to) * 0.25f, offset + to + th * 0.5f ), wpos + ImVec2( px + (ty - to) * 0.25f, offset + to + th * 0.5f ), wpos + ImVec2( px, offset + to + th ), 0xFF2222FF );
draw->AddTriangle( wpos + ImVec2( px - (ty - to) * 0.25f, offset + to + th * 0.5f ), wpos + ImVec2( px + (ty - to) * 0.25f, offset + to + th * 0.5f ), wpos + ImVec2( px, offset + to + th ), 0xFF2222FF, 2.0f );
const auto dlkText = "deadlock";
const auto dtw = ImGui::CalcTextSize( dlkText ).x;
DrawTextContrast( draw, wpos + ImVec2( px - dtw * 0.5f, offset + to + th * 0.5f - ty ), 0xFF2222FF, dlkText );
if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px - (ty - to) * 0.5 - 1, offset ), wpos + ImVec2( px + (ty - to) * 0.5 + 1, offset + ty ) ) )
{
DeadlockTooltip( dlk, dmembers );
if( IsMouseClicked( ImGuiMouseButton_Left ) )
{
m_lockInfoWindow = m.lock;
}
if( IsMouseClicked( ImGuiMouseButton_Middle ) )
{
CenterAtTime( m.waitTime );
}
}
}
}
}
}
void View::DrawThreadOverlays( const ThreadData& thread, const ImVec2& ul, const ImVec2& dr )