#include "TracyPrint.hpp" #include "TracyView.hpp" namespace tracy { void View::DrawCpuDataWindow() { const auto scale = GetScale(); ImGui::SetNextWindowSize( ImVec2( 700 * scale, 800 * scale ), ImGuiCond_FirstUseEver ); ImGui::Begin( "CPU data", &m_showCpuDataWindow ); if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; } struct PidData { std::vector tids; CpuThreadData data; }; const auto& ctd = m_worker.GetCpuThreadData(); unordered_flat_map pids; for( auto& v : ctd ) { uint64_t pid = m_worker.GetPidFromTid( v.first ); auto it = pids.find( pid ); if( it == pids.end() ) { it = pids.emplace( pid, PidData {} ).first; } it->second.tids.emplace_back( v.first ); it->second.data.runningTime += v.second.runningTime; it->second.data.runningRegions += v.second.runningRegions; it->second.data.migrations += v.second.migrations; } TextFocused( "Tracked threads:", RealToString( ctd.size() ) ); ImGui::SameLine(); TextFocused( "Tracked processes:", RealToString( pids.size() ) ); ImGui::Separator(); ImGui::BeginChild( "##cpudata" ); if( ImGui::BeginTable( "##cpudata", 5, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY ) ) { ImGui::TableSetupScrollFreeze( 0, 1 ); ImGui::TableSetupColumn( "PID/TID", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize ); ImGui::TableSetupColumn( "Name" ); ImGui::TableSetupColumn( "Running time", ImGuiTableColumnFlags_PreferSortDescending ); ImGui::TableSetupColumn( "Running regions", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize ); ImGui::TableSetupColumn( "CPU migrations", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize ); ImGui::TableHeadersRow(); std::vector::iterator> psort; psort.reserve( pids.size() ); for( auto it = pids.begin(); it != pids.end(); ++it ) psort.emplace_back( it ); const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs; switch( sortspec.ColumnIndex ) { case 0: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( psort.begin(), psort.end(), [] ( const auto& l, const auto& r ) { return l->first > r->first; } ); } else { pdqsort_branchless( psort.begin(), psort.end(), [] ( const auto& l, const auto& r ) { return l->first < r->first; } ); } break; case 1: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( psort.begin(), psort.end(), [this] ( const auto& l, const auto& r ) { return strcmp( m_worker.GetExternalName( l->second.tids[0] ).first, m_worker.GetExternalName( r->second.tids[0] ).first ) > 0; } ); } else { pdqsort_branchless( psort.begin(), psort.end(), [this] ( const auto& l, const auto& r ) { return strcmp( m_worker.GetExternalName( l->second.tids[0] ).first, m_worker.GetExternalName( r->second.tids[0] ).first ) < 0; } ); } break; case 2: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( psort.begin(), psort.end(), [] ( const auto& l, const auto& r ) { return l->second.data.runningTime > r->second.data.runningTime; } ); } else { pdqsort_branchless( psort.begin(), psort.end(), [] ( const auto& l, const auto& r ) { return l->second.data.runningTime < r->second.data.runningTime; } ); } break; case 3: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( psort.begin(), psort.end(), [] ( const auto& l, const auto& r ) { return l->second.data.runningRegions > r->second.data.runningRegions; } ); } else { pdqsort_branchless( psort.begin(), psort.end(), [] ( const auto& l, const auto& r ) { return l->second.data.runningRegions < r->second.data.runningRegions; } ); } break; case 4: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( psort.begin(), psort.end(), [] ( const auto& l, const auto& r ) { return l->second.data.migrations > r->second.data.migrations; } ); } else { pdqsort_branchless( psort.begin(), psort.end(), [] ( const auto& l, const auto& r ) { return l->second.data.migrations < r->second.data.migrations; } ); } break; default: assert( false ); break; } const auto thisPid = m_worker.GetPid(); const auto rtimespan = 1.0 / m_worker.GetLastTime(); const auto ty = ImGui::GetTextLineHeight(); auto& style = ImGui::GetStyle(); const auto framePaddingY = style.FramePadding.y; for( auto& pidit : psort ) { ImGui::TableNextRow(); ImGui::TableNextColumn(); char buf[128]; auto& pid = *pidit; const auto pidMatch = thisPid != 0 && thisPid == pid.first; auto name = m_worker.GetExternalName( pid.second.tids[0] ).first; if( pidMatch ) { name = m_worker.GetCaptureProgram().c_str(); ImGui::PushStyleColor( ImGuiCol_Text, ImVec4( 0.2f, 1.0f, 0.2f, 1.0f ) ); } const auto pidtxt = pid.first == 0 ? "Unknown" : RealToString( pid.first ); const auto expand = ImGui::TreeNode( pidtxt ); if( ImGui::IsItemHovered() ) { if( pidMatch ) { m_drawThreadMigrations = pid.first; m_cpuDataThread = pid.first; } m_drawThreadHighlight = pid.first; } const auto tsz = pid.second.tids.size(); if( tsz > 1 ) { ImGui::SameLine(); ImGui::TextDisabled( "(%s)", RealToString( tsz ) ); } ImGui::TableNextColumn(); ImGui::TextUnformatted( pid.first == 0 ? "???" : name ); if( ImGui::IsItemHovered() ) { if( pidMatch ) { m_drawThreadMigrations = pid.first; m_cpuDataThread = pid.first; } m_drawThreadHighlight = pid.first; } ImGui::TableNextColumn(); PrintStringPercent( buf, TimeToString( pid.second.data.runningTime ), double( pid.second.data.runningTime ) * rtimespan * 100 ); style.FramePadding.y = 0; ImGui::ProgressBar( double( pid.second.data.runningTime ) * rtimespan, ImVec2( -1, ty ), buf ); style.FramePadding.y = framePaddingY; ImGui::TableNextColumn(); ImGui::TextUnformatted( RealToString( pid.second.data.runningRegions ) ); ImGui::TableNextColumn(); ImGui::TextUnformatted( RealToString( pid.second.data.migrations ) ); ImGui::SameLine(); PrintStringPercent( buf, double( pid.second.data.migrations ) / pid.second.data.runningRegions * 100 ); TextDisabledUnformatted( buf ); if( expand ) { ImGui::Separator(); switch( sortspec.ColumnIndex ) { case 0: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), []( const auto& l, const auto& r ) { return l > r; } ); } else { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end() ); } break; case 1: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), [this] ( const auto& l, const auto& r ) { return strcmp( m_worker.GetExternalName( l ).second, m_worker.GetExternalName( r ).second ) > 0; } ); } else { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), [this] ( const auto& l, const auto& r ) { return strcmp( m_worker.GetExternalName( l ).second, m_worker.GetExternalName( r ).second ) < 0; } ); } break; case 2: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), [&ctd] ( const auto& l, const auto& r ) { return ctd.find( l )->second.runningTime > ctd.find( r )->second.runningTime; } ); } else { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), [&ctd] ( const auto& l, const auto& r ) { return ctd.find( l )->second.runningTime < ctd.find( r )->second.runningTime; } ); } break; case 3: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), [&ctd] ( const auto& l, const auto& r ) { return ctd.find( l )->second.runningRegions > ctd.find( r )->second.runningRegions; } ); } else { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), [&ctd] ( const auto& l, const auto& r ) { return ctd.find( l )->second.runningRegions < ctd.find( r )->second.runningRegions; } ); } break; case 4: if( sortspec.SortDirection == ImGuiSortDirection_Descending ) { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), [&ctd] ( const auto& l, const auto& r ) { return ctd.find( l )->second.migrations > ctd.find( r )->second.migrations; } ); } else { pdqsort_branchless( pid.second.tids.begin(), pid.second.tids.end(), [&ctd] ( const auto& l, const auto& r ) { return ctd.find( l )->second.migrations < ctd.find( r )->second.migrations; } ); } break; default: assert( false ); break; } for( auto& tid : pid.second.tids ) { ImGui::TableNextRow(); ImGui::TableNextColumn(); const auto tidMatch = pidMatch && m_worker.IsThreadLocal( tid ); const char* tname; if( tidMatch ) { tname = m_worker.GetThreadName( tid ); ImGui::PushStyleColor( ImGuiCol_Text, ImVec4( 1.0f, 1.0f, 0.2f, 1.0f ) ); } else { tname = m_worker.GetExternalName( tid ).second; } const auto& tit = ctd.find( tid ); assert( tit != ctd.end() ); ImGui::TextUnformatted( RealToString( tid ) ); if( ImGui::IsItemHovered() ) { if( tidMatch ) { m_drawThreadMigrations = tid; m_cpuDataThread = tid; } m_drawThreadHighlight = tid; } ImGui::TableNextColumn(); if( tidMatch ) { SmallColorBox( GetThreadColor( tid, 0 ) ); ImGui::SameLine(); } ImGui::TextUnformatted( tname ); if( ImGui::IsItemHovered() ) { if( tidMatch ) { m_drawThreadMigrations = tid; m_cpuDataThread = tid; } m_drawThreadHighlight = tid; } ImGui::TableNextColumn(); PrintStringPercent( buf, TimeToString( tit->second.runningTime ), double( tit->second.runningTime ) * rtimespan * 100 ); style.FramePadding.y = 0; ImGui::ProgressBar( double( tit->second.runningTime ) * rtimespan, ImVec2( -1, ty ), buf ); style.FramePadding.y = framePaddingY; ImGui::TableNextColumn(); ImGui::TextUnformatted( RealToString( tit->second.runningRegions ) ); ImGui::TableNextColumn(); ImGui::TextUnformatted( RealToString( tit->second.migrations ) ); ImGui::SameLine(); PrintStringPercent( buf, double( tit->second.migrations ) / tit->second.runningRegions * 100 ); TextDisabledUnformatted( buf ); if( tidMatch ) { ImGui::PopStyleColor(); } } ImGui::TreePop(); ImGui::Separator(); } if( pidMatch ) { ImGui::PopStyleColor(); } } ImGui::EndTable(); } ImGui::EndChild(); ImGui::End(); } }