diff --git a/server/TracyView.cpp b/server/TracyView.cpp index 999b0e91..19f3cafb 100644 --- a/server/TracyView.cpp +++ b/server/TracyView.cpp @@ -16320,68 +16320,140 @@ void View::DrawRangeEntry( Range& range, const char* label, uint32_t color, cons void View::DrawWaitStacks() { ImGui::SetNextWindowSize( ImVec2( 1400, 500 ), ImGuiCond_FirstUseEver ); - ImGui::Begin( "Wait stacks", &m_showWaitStacks, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse ); + ImGui::Begin( "Wait stacks", &m_showWaitStacks ); #ifdef TRACY_NO_STATISTICS ImGui::TextWrapped( "Rebuild without the TRACY_NO_STATISTICS macro to enable wait stacks." ); #else + uint64_t totalCount = 0; unordered_flat_map stacks; - auto& td = m_worker.GetThreadData(); - for( auto& t : td ) + for( auto& t : m_threadOrder ) { - for( auto& sd : t->ctxSwitchSamples ) + if( WaitStackThread( t->id ) ) { - auto cs = sd.callstack.Val(); - auto it = stacks.find( cs ); - if( it == stacks.end() ) + totalCount += t->ctxSwitchSamples.size(); + for( auto& sd : t->ctxSwitchSamples ) { - stacks.emplace( cs, 1 ); - } - else - { - it->second++; + auto cs = sd.callstack.Val(); + auto it = stacks.find( cs ); + if( it == stacks.end() ) + { + stacks.emplace( cs, 1 ); + } + else + { + it->second++; + } } } } - const auto totalSamples = m_worker.GetContextSwitchSampleCount(); - TextFocused( "Total wait stacks:", RealToString( totalSamples ) ); + TextFocused( "Total wait stacks:", RealToString( m_worker.GetContextSwitchSampleCount() ) ); ImGui::SameLine(); ImGui::Spacing(); ImGui::SameLine(); - TextDisabledUnformatted( "Wait stack:" ); + TextFocused( "Selected:", RealToString( totalCount ) ); + + bool threadsChanged = false; + auto expand = ImGui::TreeNode( ICON_FA_RANDOM " Visible threads:" ); ImGui::SameLine(); - if( ImGui::SmallButton( " " ICON_FA_CARET_LEFT " " ) ) + ImGui::TextDisabled( "(%zu)", m_threadOrder.size() ); + if( expand ) { - m_waitStack = std::max( m_waitStack - 1, 0 ); + auto& crash = m_worker.GetCrashEvent(); + + ImGui::SameLine(); + if( ImGui::SmallButton( "Select all" ) ) + { + for( const auto& t : m_threadOrder ) + { + WaitStackThread( t->id ) = true; + } + threadsChanged = true; + } + ImGui::SameLine(); + if( ImGui::SmallButton( "Unselect all" ) ) + { + for( const auto& t : m_threadOrder ) + { + WaitStackThread( t->id ) = false; + } + threadsChanged = true; + } + + int idx = 0; + for( const auto& t : m_threadOrder ) + { + if( t->ctxSwitchSamples.empty() ) continue; + ImGui::PushID( idx++ ); + const auto threadColor = GetThreadColor( t->id, 0 ); + SmallColorBox( threadColor ); + ImGui::SameLine(); + if( SmallCheckbox( m_worker.GetThreadName( t->id ), &WaitStackThread( t->id ) ) ) + { + threadsChanged = true; + } + ImGui::PopID(); + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( t->ctxSwitchSamples.size() ) ); + if( crash.thread == t->id ) + { + ImGui::SameLine(); + TextColoredUnformatted( ImVec4( 1.f, 0.2f, 0.2f, 1.f ), ICON_FA_SKULL " Crashed" ); + } + if( t->isFiber ) + { + ImGui::SameLine(); + TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" ); + } + } + ImGui::TreePop(); } - ImGui::SameLine(); - ImGui::Text( "%s / %s", RealToString( m_waitStack + 1 ), RealToString( stacks.size() ) ); - if( ImGui::IsItemClicked() ) ImGui::OpenPopup( "WaitStacksPopup" ); - ImGui::SameLine(); - if( ImGui::SmallButton( " " ICON_FA_CARET_RIGHT " " ) ) - { - m_waitStack = std::min( m_waitStack + 1, stacks.size() - 1 ); - } - if( ImGui::BeginPopup( "WaitStacksPopup" ) ) - { - int sel = m_waitStack + 1; - ImGui::SetNextItemWidth( 120 ); - const bool clicked = ImGui::InputInt( "##waitStack", &sel, 1, 100, ImGuiInputTextFlags_EnterReturnsTrue ); - if( clicked ) m_waitStack = std::min( std::max( sel, 1 ), int( stacks.size() ) ) - 1; - ImGui::EndPopup(); - } - Vector data; - data.reserve( stacks.size() ); - for( auto it = stacks.begin(); it != stacks.end(); ++it ) data.push_back( it ); - pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l->second > r->second; } ); - ImGui::SameLine(); - TextFocused( "Counts:", RealToString( data[m_waitStack]->second ) ); - ImGui::SameLine(); - char buf[64]; - PrintStringPercent( buf, 100. * data[m_waitStack]->second / totalSamples ); - TextDisabledUnformatted( buf ); + if( threadsChanged ) m_waitStack = 0; + ImGui::Separator(); - DrawCallstackTable( data[m_waitStack]->first, false ); + if( stacks.empty() ) + { + ImGui::TextUnformatted( "No wait stacks to display." ); + } + else + { + TextDisabledUnformatted( "Wait stack:" ); + ImGui::SameLine(); + if( ImGui::SmallButton( " " ICON_FA_CARET_LEFT " " ) ) + { + m_waitStack = std::max( m_waitStack - 1, 0 ); + } + ImGui::SameLine(); + ImGui::Text( "%s / %s", RealToString( m_waitStack + 1 ), RealToString( stacks.size() ) ); + if( ImGui::IsItemClicked() ) ImGui::OpenPopup( "WaitStacksPopup" ); + ImGui::SameLine(); + if( ImGui::SmallButton( " " ICON_FA_CARET_RIGHT " " ) ) + { + m_waitStack = std::min( m_waitStack + 1, stacks.size() - 1 ); + } + if( ImGui::BeginPopup( "WaitStacksPopup" ) ) + { + int sel = m_waitStack + 1; + ImGui::SetNextItemWidth( 120 ); + const bool clicked = ImGui::InputInt( "##waitStack", &sel, 1, 100, ImGuiInputTextFlags_EnterReturnsTrue ); + if( clicked ) m_waitStack = std::min( std::max( sel, 1 ), int( stacks.size() ) ) - 1; + ImGui::EndPopup(); + } + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + Vector data; + data.reserve( stacks.size() ); + for( auto it = stacks.begin(); it != stacks.end(); ++it ) data.push_back( it ); + pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l->second > r->second; } ); + TextFocused( "Counts:", RealToString( data[m_waitStack]->second ) ); + ImGui::SameLine(); + char buf[64]; + PrintStringPercent( buf, 100. * data[m_waitStack]->second / totalCount ); + TextDisabledUnformatted( buf ); + ImGui::Separator(); + DrawCallstackTable( data[m_waitStack]->first, false ); + } #endif ImGui::End(); } diff --git a/server/TracyView.hpp b/server/TracyView.hpp index 214701aa..7812cd00 100644 --- a/server/TracyView.hpp +++ b/server/TracyView.hpp @@ -313,6 +313,7 @@ private: unordered_flat_map m_visData; unordered_flat_map m_visibleMsgThread; + unordered_flat_map m_waitStackThread; unordered_flat_map m_gpuDrift; unordered_flat_map m_plotView; Vector m_threadOrder; @@ -338,6 +339,16 @@ private: return it->second; } + tracy_force_inline bool& WaitStackThread( uint64_t thread ) + { + auto it = m_waitStackThread.find( thread ); + if( it == m_waitStackThread.end() ) + { + it = m_waitStackThread.emplace( thread, true ).first; + } + return it->second; + } + tracy_force_inline int& GpuDrift( const void* ptr ) { auto it = m_gpuDrift.find( ptr );