Show a deadlock notification with cycle details.

This commit is contained in:
Bartosz Taudul
2026-09-22 02:21:40 +02:00
parent ae32c0fff8
commit 7a975bea44
3 changed files with 102 additions and 1 deletions

View File

@@ -758,6 +758,8 @@ bool View::DrawImpl()
return keepOpen;
}
m_worker.UpdateDeadlocks();
if( m_achievements )
{
if( m_worker.IsConnected() ) Achieve( "connectToClient" );

View File

@@ -68,6 +68,8 @@ struct MessagesDraw;
struct CpuUsageDraw;
struct CpuCtxDraw;
struct LockDraw;
struct DeadlockGroup;
struct DeadlockMember;
struct PlotDraw;
struct FlameGraphContext;
@@ -211,6 +213,12 @@ public:
void DrawThread( const TimelineContext& ctx, const ThreadData& thread, const std::vector<TimelineDraw>& draw, const std::vector<ContextSwitchDraw>& ctxDraw, const std::vector<SamplesDraw>& samplesDraw, const std::vector<std::unique_ptr<LockDraw>>& lockDraw, int& offset, int depth, bool hasCtxSwitches, bool hasSamples );
void DrawThreadMessagesList( const TimelineContext& ctx, const std::vector<MessagesDraw>& drawList, int offset, uint64_t tid );
void DrawThreadOverlays( const ThreadData& thread, const ImVec2& ul, const ImVec2& dr );
static constexpr uint64_t AnyThread = ~0ull;
static constexpr uint32_t AnyLock = ~0u;
void DrawDeadlockMembers( const DeadlockGroup& group, const Vector<DeadlockMember>& members ) const;
void DrawDeadlockDetail( uint64_t thread, uint32_t lock ) const;
bool DrawGpu( const TimelineContext& ctx, const GpuCtxData& gpu, int& offset );
bool DrawCpuData( const TimelineContext& ctx, const std::vector<CpuUsageDraw>& cpuDraw, const std::vector<std::vector<CpuCtxDraw>>& ctxDraw, int& offset, bool hasCpuData );
void DrawThreadMigrations( const TimelineContext& ctx, const int origOffset, uint64_t thread );
@@ -419,7 +427,8 @@ private:
void ShowZoneInfo( const ZoneEvent& ev );
void ShowZoneInfo( const GpuEvent& ev, uint64_t thread );
void DeadlockTooltip( const DeadlockGroup& group, const Vector<DeadlockMember>& members ) const;
void DeadlocksTooltip();
void ZoneTooltip( const ZoneEvent& ev );
void ZoneTooltip( const GpuEvent& ev );
void CallstackTooltip( uint32_t idx );

View File

@@ -10,6 +10,82 @@ namespace tracy
extern double s_time;
void View::DrawDeadlockMembers( const DeadlockGroup& group, const Vector<DeadlockMember>& members ) const
{
const auto& lockMap = m_worker.GetLockMap();
for( uint32_t i=group.first; i<group.first+group.cnt; i++ )
{
const auto& m = members[i];
const auto it = lockMap.find( m.lock );
assert( it != lockMap.end() );
if( m.holder == m.thread )
{
ImGui::Text( "\"%s\" waits for %u: %s (upgrade request)", m_worker.GetThreadName( m.thread ), m.lock, GetLockDisplayName( m_worker, *it->second ) );
}
else
{
ImGui::Text( "\"%s\" waits for %u: %s, held by \"%s\"", m_worker.GetThreadName( m.thread ), m.lock, GetLockDisplayName( m_worker, *it->second ), m_worker.GetThreadName( m.holder ) );
}
}
}
void View::DrawDeadlockDetail( uint64_t thread, uint32_t lock ) const
{
const auto& dgroups = m_worker.GetDeadlockGroups();
const auto& dmembers = m_worker.GetDeadlockMembers();
for( const auto& g : dgroups )
{
bool match = false;
for( uint32_t i=g.first; i<g.first+g.cnt; i++ )
{
const auto& m = dmembers[i];
if( ( thread == AnyThread || m.thread == thread ) && ( lock == AnyLock || m.lock == lock ) )
{
match = true;
break;
}
}
if( !match ) continue;
ImGui::Separator();
TextColoredUnformatted( ImVec4( 1.f, 0.2f, 0.2f, 1.f ), "Deadlock detected" );
TextFocused( "Cycle closed at:", TimeToString( g.time ) );
DrawDeadlockMembers( g, dmembers );
}
}
void View::DeadlockTooltip( const DeadlockGroup& group, const Vector<DeadlockMember>& members ) const
{
ImGui::BeginTooltip();
ImGui::TextUnformatted( "Deadlock detected" );
TextFocused( "Cycle closed at:", TimeToString( group.time ) );
ImGui::Separator();
DrawDeadlockMembers( group, members );
ImGui::EndTooltip();
}
void View::DeadlocksTooltip()
{
const auto& groups = m_worker.GetDeadlockGroups();
const auto& members = m_worker.GetDeadlockMembers();
if( groups.size() == 1 )
{
DeadlockTooltip( groups[0], members );
return;
}
ImGui::BeginTooltip();
ImGui::Text( "%zu deadlocks detected", groups.size() );
for( size_t gi=0; gi<groups.size(); gi++ )
{
const auto& g = groups[gi];
ImGui::Separator();
ImGui::Text( "Cycle #%zu closed at: %s", gi+1, TimeToString( g.time ) );
ImGui::Indent( ImGui::GetTextLineHeight() );
DrawDeadlockMembers( g, members );
ImGui::Unindent( ImGui::GetTextLineHeight() );
}
ImGui::EndTooltip();
}
void View::DrawNotificationArea()
{
if( m_sendQueueWarning.enabled )
@@ -90,6 +166,20 @@ void View::DrawNotificationArea()
}
}
}
const auto& deadlocks = m_worker.GetDeadlockGroups();
if( !deadlocks.empty() )
{
ImGui::SameLine();
TextColoredUnformatted( ImVec4( 1, 0, 0, 1 ), ICON_FA_ARROWS_SPIN );
if( ImGui::IsItemHovered() )
{
DeadlocksTooltip();
if( IsMouseClicked( ImGuiMouseButton_Left ) )
{
m_showInfo = true;
}
}
}
if( m_worker.AreSamplesInconsistent() )
{
ImGui::SameLine();