diff --git a/server/TracyView.cpp b/server/TracyView.cpp index 6620f223..999b0e91 100644 --- a/server/TracyView.cpp +++ b/server/TracyView.cpp @@ -925,6 +925,11 @@ bool View::DrawImpl() m_showAnnotationList = true; } ToggleButton( ICON_FA_RULER " Limits", m_showRanges ); + const auto cscnt = m_worker.GetContextSwitchSampleCount(); + if( ButtonDisablable( ICON_FA_HOURGLASS_HALF " Wait stacks", cscnt == 0 ) ) + { + m_showWaitStacks = true; + } ImGui::EndPopup(); } ImGui::SameLine(); @@ -1095,6 +1100,7 @@ bool View::DrawImpl() if( m_showAnnotationList ) DrawAnnotationList(); if( m_sampleParents.symAddr != 0 ) DrawSampleParents(); if( m_showRanges ) DrawRanges(); + if( m_showWaitStacks ) DrawWaitStacks(); if( m_setRangePopup.active ) { @@ -3972,7 +3978,7 @@ void View::DrawContextSwitches( const ContextSwitch* ctx, const Vectortime.Val() == ev.Start() ) { ImGui::Separator(); - TextDisabledUnformatted( "Wait stack:" ); + TextDisabledUnformatted( ICON_FA_HOURGLASS_HALF " Wait stack:" ); CallstackTooltipContents( sdit->callstack.Val() ); if( ImGui::IsMouseClicked( 0 ) ) { @@ -16311,6 +16317,75 @@ 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 ); +#ifdef TRACY_NO_STATISTICS + ImGui::TextWrapped( "Rebuild without the TRACY_NO_STATISTICS macro to enable wait stacks." ); +#else + unordered_flat_map stacks; + auto& td = m_worker.GetThreadData(); + for( auto& t : td ) + { + for( auto& sd : t->ctxSwitchSamples ) + { + 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 ) ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + 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(); + } + 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 ); + ImGui::Separator(); + DrawCallstackTable( data[m_waitStack]->first, false ); +#endif + ImGui::End(); +} + void View::ListMemData( std::vector& vec, std::function DrawAddress, const char* id, int64_t startTime, uint64_t pool ) { if( startTime == -1 ) startTime = 0; diff --git a/server/TracyView.hpp b/server/TracyView.hpp index cb15a7d8..214701aa 100644 --- a/server/TracyView.hpp +++ b/server/TracyView.hpp @@ -221,6 +221,7 @@ private: void DrawRanges(); void DrawRangeEntry( Range& range, const char* label, uint32_t color, const char* popupLabel, int id ); void DrawSourceTooltip( const char* filename, uint32_t line, int before = 3, int after = 3, bool separateTooltip = true ); + void DrawWaitStacks(); void ListMemData( std::vector& vec, std::function DrawAddress, const char* id = nullptr, int64_t startTime = -1, uint64_t pool = 0 ); @@ -411,6 +412,7 @@ private: bool m_showPlayback = false; bool m_showCpuDataWindow = false; bool m_showAnnotationList = false; + bool m_showWaitStacks = false; AccumulationMode m_statAccumulationMode = AccumulationMode::SelfOnly; bool m_statSampleTime = true; @@ -428,6 +430,7 @@ private: bool m_ctxSwitchTimeRelativeToZone = true; bool m_messageTimeRelativeToZone = true; uint64_t m_zoneInfoMemPool = 0; + int m_waitStack = 0; ShortcutAction m_shortcut = ShortcutAction::None; Namespace m_namespace = Namespace::Short;