diff --git a/server/TracyEvent.hpp b/server/TracyEvent.hpp index d5e0e0ba..9e35a668 100644 --- a/server/TracyEvent.hpp +++ b/server/TracyEvent.hpp @@ -158,6 +158,8 @@ private: uint8_t m_val[6]; }; +struct Int48Sort { bool operator()( const Int48& lhs, const Int48& rhs ) { return lhs.Val() < rhs.Val(); }; }; + struct SourceLocationBase { @@ -253,12 +255,33 @@ enum { SampleDataRangeSize = sizeof( SampleDataRange ) }; struct HwSampleData { - uint32_t cycles; - uint32_t retired; - uint32_t cacheRef; - uint32_t cacheMiss; - uint32_t branchRetired; - uint32_t branchMiss; + SortedVector cycles; + SortedVector retired; + SortedVector cacheRef; + SortedVector cacheMiss; + SortedVector branchRetired; + SortedVector branchMiss; + + bool is_sorted() const + { + return + cycles.is_sorted() && + retired.is_sorted() && + cacheRef.is_sorted() && + cacheMiss.is_sorted() && + branchRetired.is_sorted() && + branchMiss.is_sorted(); + } + + void sort() + { + if( !cycles.is_sorted() ) cycles.sort(); + if( !retired.is_sorted() ) retired.sort(); + if( !cacheRef.is_sorted() ) cacheRef.sort(); + if( !cacheMiss.is_sorted() ) cacheMiss.sort(); + if( !branchRetired.is_sorted() ) branchRetired.sort(); + if( !branchMiss.is_sorted() ) branchMiss.sort(); + } }; enum { HwSampleDataSize = sizeof( HwSampleData ) }; diff --git a/server/TracySourceView.cpp b/server/TracySourceView.cpp index 5b17b5a4..3963f6ec 100644 --- a/server/TracySourceView.cpp +++ b/server/TracySourceView.cpp @@ -2633,9 +2633,9 @@ void SourceView::RenderLine( const Tokenizer::Line& line, int lineNum, const Add DrawLine( draw, dpos + ImVec2( 0, ty+2 ), dpos + ImVec2( w, ty+2 ), 0x08FFFFFF ); } -static void PrintHwSampleTooltip( const HwSampleData& hw, bool hideFirstSeparator ) +static void PrintHwSampleTooltip( size_t cycles, size_t retired, size_t cacheRef, size_t cacheMiss, size_t branchRetired, size_t branchMiss, bool hideFirstSeparator ) { - if( hw.cycles || hw.retired ) + if( cycles || retired ) { if( hideFirstSeparator ) { @@ -2645,17 +2645,17 @@ static void PrintHwSampleTooltip( const HwSampleData& hw, bool hideFirstSeparato { ImGui::Separator(); } - if( hw.cycles && hw.retired ) + if( cycles && retired ) { char buf[32]; - auto end = PrintFloat( buf, buf+32, float( hw.retired ) / hw.cycles, 2 ); + auto end = PrintFloat( buf, buf+32, float( retired ) / cycles, 2 ); *end = '\0'; TextFocused( "IPC:", buf ); } - if( hw.cycles ) TextFocused( "Cycles:", RealToString( hw.cycles ) ); - if( hw.retired ) TextFocused( "Retirements:", RealToString( hw.retired ) ); + if( cycles ) TextFocused( "Cycles:", RealToString( cycles ) ); + if( retired ) TextFocused( "Retirements:", RealToString( retired ) ); } - if( hw.cacheRef || hw.cacheMiss ) + if( cacheRef || cacheMiss ) { if( hideFirstSeparator ) { @@ -2665,17 +2665,17 @@ static void PrintHwSampleTooltip( const HwSampleData& hw, bool hideFirstSeparato { ImGui::Separator(); } - if( hw.cacheRef ) + if( cacheRef ) { char buf[32]; - auto end = PrintFloat( buf, buf+32, float( 100 * hw.cacheMiss ) / hw.cacheRef, 2 ); + auto end = PrintFloat( buf, buf+32, float( 100 * cacheMiss ) / cacheRef, 2 ); memcpy( end, "%", 2 ); TextFocused( "Cache miss rate:", buf ); - TextFocused( "Cache references:", RealToString( hw.cacheRef ) ); + TextFocused( "Cache references:", RealToString( cacheRef ) ); } - if( hw.cacheMiss ) TextFocused( "Cache misses:", RealToString( hw.cacheMiss ) ); + if( cacheMiss ) TextFocused( "Cache misses:", RealToString( cacheMiss ) ); } - if( hw.branchRetired || hw.branchMiss ) + if( branchRetired || branchMiss ) { if( hideFirstSeparator ) { @@ -2685,15 +2685,15 @@ static void PrintHwSampleTooltip( const HwSampleData& hw, bool hideFirstSeparato { ImGui::Separator(); } - if( hw.branchRetired ) + if( branchRetired ) { char buf[32]; - auto end = PrintFloat( buf, buf+32, float( 100 * hw.branchMiss ) / hw.branchRetired, 2 ); + auto end = PrintFloat( buf, buf+32, float( 100 * branchMiss ) / branchRetired, 2 ); memcpy( end, "%", 2 ); TextFocused( "Branch mispredictions rate:", buf ); - TextFocused( "Retired branches:", RealToString( hw.branchRetired ) ); + TextFocused( "Retired branches:", RealToString( branchRetired ) ); } - if( hw.branchMiss ) TextFocused( "Branch mispredictions:", RealToString( hw.branchMiss ) ); + if( branchMiss ) TextFocused( "Branch mispredictions:", RealToString( branchMiss ) ); } } @@ -2719,6 +2719,18 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr const auto asmIdx = &line - m_asm.data(); + const auto hw = worker.GetHwSampleData( line.addr ); + size_t cycles = 0, retired = 0, cacheRef = 0, cacheMiss = 0, branchRetired = 0, branchMiss = 0; + if( hw ) + { + cycles = hw->cycles.size(); + retired = hw->retired.size(); + cacheRef = hw->cacheRef.size(); + cacheMiss = hw->cacheMiss.size(); + branchRetired = hw->branchRetired.size(); + branchMiss = hw->branchMiss.size(); + } + const auto ts = ImGui::CalcTextSize( " " ); if( iptotal.local + iptotal.ext != 0 ) { @@ -2732,7 +2744,7 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr { if( m_font ) ImGui::PopFont(); ImGui::BeginTooltip(); - PrintHwSampleTooltip( *hw, true ); + PrintHwSampleTooltip( cycles, retired, cacheRef, cacheMiss, branchRetired, branchMiss, true ); ImGui::EndTooltip(); if( m_font ) ImGui::PushFont( m_font ); } @@ -2788,7 +2800,7 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr } const auto hw = worker.GetHwSampleData( line.addr ); - if( hw ) PrintHwSampleTooltip( *hw, false ); + if( hw ) PrintHwSampleTooltip( cycles, retired, cacheRef, cacheMiss, branchRetired, branchMiss, false ); const auto& stats = *worker.GetSymbolStats( symAddrParents ); if( !stats.parents.empty() ) @@ -2884,13 +2896,12 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr const bool showHwSamples = m_hwSamples && worker.GetHwSampleCountAddress() != 0; if( showHwSamples ) { - auto hw = worker.GetHwSampleData( line.addr ); if( hw ) { - if( hw->cycles != 0 ) + if( cycles ) { - const bool unreliable = hw->cycles < 10 || hw->retired < 10; - const float ipc = float( hw->retired ) / hw->cycles; + const bool unreliable = cycles < 10 || retired < 10; + const float ipc = float( retired ) / cycles; uint32_t col = unreliable ? 0x44FFFFFF : GetGoodnessColor( ipc * 0.25f ); if( ipc >= 10 ) { @@ -2913,8 +2924,8 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr ImGui::SameLine(); TextDisabledUnformatted( "Higher is better" ); ImGui::Separator(); - TextFocused( "Cycles:", RealToString( hw->cycles ) ); - TextFocused( "Retirements:", RealToString( hw->retired ) ); + TextFocused( "Cycles:", RealToString( cycles ) ); + TextFocused( "Retirements:", RealToString( retired ) ); if( unreliable ) TextColoredUnformatted( 0xFF4444FF, "Not enough samples for reliable data!" ); ImGui::EndTooltip(); if( m_font ) ImGui::PushFont( m_font ); @@ -2925,12 +2936,12 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr ImGui::ItemSize( ImVec2( 7 * ts.x, ts.y ) ); } ImGui::SameLine( 0, 0 ); - if( hw->branchRetired != 0 ) + if( branchRetired ) { - const bool unreliable = hw->branchRetired < 10; - const float rate = float( hw->branchMiss ) / hw->branchRetired; + const bool unreliable = branchRetired < 10; + const float rate = float( branchMiss ) / branchRetired; uint32_t col = unreliable ? 0x44FFFFFF : GetGoodnessColor( 1.f - rate * 3.f ); - if( hw->branchMiss == 0 ) + if( branchMiss == 0 ) { TextColoredUnformatted( col, " 0% " ); } @@ -2963,8 +2974,8 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr ImGui::SameLine(); TextDisabledUnformatted( "Lower is better" ); ImGui::Separator(); - TextFocused( "Retired branches:", RealToString( hw->branchRetired ) ); - TextFocused( "Branch mispredictions:", RealToString( hw->branchMiss ) ); + TextFocused( "Retired branches:", RealToString( branchRetired ) ); + TextFocused( "Branch mispredictions:", RealToString( branchMiss ) ); if( unreliable ) TextColoredUnformatted( 0xFF4444FF, "Not enough samples for reliable data!" ); ImGui::EndTooltip(); if( m_font ) ImGui::PushFont( m_font ); @@ -2975,12 +2986,12 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr ImGui::ItemSize( ImVec2( 7 * ts.x, ts.y ) ); } ImGui::SameLine( 0, 0 ); - if( hw->cacheRef != 0 ) + if( cacheRef ) { - const bool unreliable = hw->cacheRef < 10; - const float rate = float( hw->cacheMiss ) / hw->cacheRef; + const bool unreliable = cacheRef < 10; + const float rate = float( cacheMiss ) / cacheRef; uint32_t col = unreliable ? 0x44FFFFFF : GetGoodnessColor( 1.f - rate * 3.f ); - if( hw->cacheMiss == 0 ) + if( cacheMiss == 0 ) { TextColoredUnformatted( col, " 0%" ); } @@ -3013,8 +3024,8 @@ void SourceView::RenderAsmLine( AsmLine& line, const AddrStat& ipcnt, const Addr ImGui::SameLine(); TextDisabledUnformatted( "Lower is better" ); ImGui::Separator(); - TextFocused( "Cache references:", RealToString( hw->cacheRef ) ); - TextFocused( "Cache misses:", RealToString( hw->cacheMiss ) ); + TextFocused( "Cache references:", RealToString( cacheRef ) ); + TextFocused( "Cache misses:", RealToString( cacheMiss ) ); if( unreliable ) TextColoredUnformatted( 0xFF4444FF, "Not enough samples for reliable data!" ); ImGui::EndTooltip(); if( m_font ) ImGui::PushFont( m_font ); diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 8c10bd11..ab96ee7f 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -237,6 +237,23 @@ static tracy_force_inline void UpdateLockRange( LockMap& lockmap, const LockEven if( range.end < lt ) range.end = lt; } +template +static void ReadHwSampleVec( FileRead& f, SortedVector& vec, Slab& slab ) +{ + uint64_t sz; + f.Read( sz ); + if( sz != 0 ) + { + int64_t refTime = 0; + vec.reserve_exact( sz, slab ); + for( uint64_t i=0; isecond; + ReadHwSampleVec( f, data.cycles, m_slab ); + ReadHwSampleVec( f, data.retired, m_slab ); + ReadHwSampleVec( f, data.cacheRef, m_slab ); + ReadHwSampleVec( f, data.cacheMiss, m_slab ); + ReadHwSampleVec( f, data.branchRetired, m_slab ); + ReadHwSampleVec( f, data.branchMiss, m_slab ); } } @@ -6323,44 +6345,50 @@ void Worker::ProcessTidToPid( const QueueTidToPid& ev ) void Worker::ProcessHwSampleCpuCycle( const QueueHwSample& ev ) { + const auto time = TscTime( ev.time - m_data.baseTime ); auto it = m_data.hwSamples.find( ev.ip ); if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first; - it->second.cycles++; + it->second.cycles.push_back( time ); } void Worker::ProcessHwSampleInstructionRetired( const QueueHwSample& ev ) { + const auto time = TscTime( ev.time - m_data.baseTime ); auto it = m_data.hwSamples.find( ev.ip ); if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first; - it->second.retired++; + it->second.retired.push_back( time ); } void Worker::ProcessHwSampleCacheReference( const QueueHwSample& ev ) { + const auto time = TscTime( ev.time - m_data.baseTime ); auto it = m_data.hwSamples.find( ev.ip ); if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first; - it->second.cacheRef++; + it->second.cacheRef.push_back( time ); } void Worker::ProcessHwSampleCacheMiss( const QueueHwSample& ev ) { + const auto time = TscTime( ev.time - m_data.baseTime ); auto it = m_data.hwSamples.find( ev.ip ); if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first; - it->second.cacheMiss++; + it->second.cacheMiss.push_back( time ); } void Worker::ProcessHwSampleBranchRetired( const QueueHwSample& ev ) { + const auto time = TscTime( ev.time - m_data.baseTime ); auto it = m_data.hwSamples.find( ev.ip ); if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first; - it->second.branchRetired++; + it->second.branchRetired.push_back( time ); } void Worker::ProcessHwSampleBranchMiss( const QueueHwSample& ev ) { + const auto time = TscTime( ev.time - m_data.baseTime ); auto it = m_data.hwSamples.find( ev.ip ); if( it == m_data.hwSamples.end() ) it = m_data.hwSamples.emplace( ev.ip, HwSampleData {} ).first; - it->second.branchMiss++; + it->second.branchMiss.push_back( time ); } void Worker::ProcessParamSetup( const QueueParamSetup& ev ) @@ -7023,6 +7051,21 @@ void Worker::Disconnect() m_disconnect = true; } +static void WriteHwSampleVec( FileWrite& f, SortedVector& vec ) +{ + uint64_t sz = vec.size(); + f.Write( &sz, sizeof( sz ) ); + if( sz != 0 ) + { + if( !vec.is_sorted() ) vec.sort(); + int64_t refTime = 0; + for( auto& v : vec ) + { + WriteTimeOffset( f, refTime, v.Val() ); + } + } +} + void Worker::Write( FileWrite& f, bool fiDict ) { DoPostponedWork(); @@ -7563,7 +7606,12 @@ void Worker::Write( FileWrite& f, bool fiDict ) for( auto& v : m_data.hwSamples ) { f.Write( &v.first, sizeof( v.first ) ); - f.Write( &v.second, sizeof( v.second ) ); + WriteHwSampleVec( f, v.second.cycles ); + WriteHwSampleVec( f, v.second.retired ); + WriteHwSampleVec( f, v.second.cacheRef ); + WriteHwSampleVec( f, v.second.cacheMiss ); + WriteHwSampleVec( f, v.second.branchRetired ); + WriteHwSampleVec( f, v.second.branchMiss ); } sz = m_data.sourceFileCache.size(); @@ -7775,8 +7823,12 @@ uint64_t Worker::GetHwSampleCount() const uint64_t cnt = 0; for( auto& v : m_data.hwSamples ) { - cnt += v.second.cycles; - cnt += v.second.retired; + cnt += v.second.cycles.size(); + cnt += v.second.retired.size(); + cnt += v.second.cacheRef.size(); + cnt += v.second.cacheMiss.size(); + cnt += v.second.branchRetired.size(); + cnt += v.second.branchMiss.size(); } return cnt; }