From c880163f960e9006c8d9ec3064b0e6ce97adbfbd Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Sat, 25 Jul 2026 19:31:40 +0200 Subject: [PATCH] Sort child sample vectors lazily. During live capture, child samples were appended to their per-address vectors in arrival order. Samples postponed due to missing context switch data are replayed after newer samples were already processed, so the vectors could become unordered. All range-limited queries binary search these vectors by time and would silently return wrong results. Inserting in sorted order at collection time would require a mid-vector insertion for every stack frame of every replayed sample, so instead the vectors are now SortedVector and are sorted lazily when accessed, following what the inline symbol list already does. This also restores proper query results for traces with inconsistent sample order. --- profiler/src/profiler/TracyDisassembly.cpp | 2 +- profiler/src/profiler/TracyDisassembly.hpp | 2 +- server/TracyEvent.hpp | 2 ++ server/TracyWorker.cpp | 16 ++++++++++------ server/TracyWorker.hpp | 4 ++-- 5 files changed, 16 insertions(+), 10 deletions(-) diff --git a/profiler/src/profiler/TracyDisassembly.cpp b/profiler/src/profiler/TracyDisassembly.cpp index c35b4b8f..aba7d3ce 100644 --- a/profiler/src/profiler/TracyDisassembly.cpp +++ b/profiler/src/profiler/TracyDisassembly.cpp @@ -863,7 +863,7 @@ void GatherIpStats( uint64_t baseAddr, AddrStatData& as, const Worker& worker, b } } -void GatherAdditionalIpStats( uint64_t baseAddr, AddrStatData& as, const Worker& worker, bool limitView, const View& view, const char* filename, bool propagateInlines ) +void GatherAdditionalIpStats( uint64_t baseAddr, AddrStatData& as, Worker& worker, bool limitView, const View& view, const char* filename, bool propagateInlines ) { if( !worker.AreSymbolSamplesReady() ) return; auto sym = worker.GetSymbolData( baseAddr ); diff --git a/profiler/src/profiler/TracyDisassembly.hpp b/profiler/src/profiler/TracyDisassembly.hpp index 58eea6ad..e074d770 100644 --- a/profiler/src/profiler/TracyDisassembly.hpp +++ b/profiler/src/profiler/TracyDisassembly.hpp @@ -138,7 +138,7 @@ std::string FormatDisassemblyLine( const AsmLine& opcode, Worker& worker, std::v nlohmann::json JsonDisassembly( uint64_t symAddr, Worker& worker, const View& view ); void GatherIpStats( uint64_t baseAddr, AddrStatData& as, const Worker& worker, bool limitView, const View& view, const char* filename, bool propagateInlines ); -void GatherAdditionalIpStats( uint64_t baseAddr, AddrStatData& as, const Worker& worker, bool limitView, const View& view, const char* filename, bool propagateInlines ); +void GatherAdditionalIpStats( uint64_t baseAddr, AddrStatData& as, Worker& worker, bool limitView, const View& view, const char* filename, bool propagateInlines ); } diff --git a/server/TracyEvent.hpp b/server/TracyEvent.hpp index 2bc39ce3..bbb26d63 100644 --- a/server/TracyEvent.hpp +++ b/server/TracyEvent.hpp @@ -685,6 +685,8 @@ struct ChildSample uint64_t addr; }; +struct ChildSampleSort { bool operator()( const ChildSample& lhs, const ChildSample& rhs ) const { return lhs.time.Val() < rhs.time.Val(); }; }; + struct SectionItem { diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index c34af597..bc02f41a 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -1957,11 +1957,11 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow auto it = m_data.childSamples.find( addr ); if( it == m_data.childSamples.end() ) { - m_data.childSamples.emplace( addr, Vector( ChildSample { time, childAddr } ) ); + m_data.childSamples.emplace( addr, SortedVector( ChildSample { time, childAddr } ) ); } else { - it->second.push_back_non_empty( ChildSample { time, childAddr } ); + it->second.push_back( ChildSample { time, childAddr } ); } childAddr = addr; } @@ -1973,7 +1973,7 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks, bool allow } for( auto& v : m_data.childSamples ) { - pdqsort_branchless( v.second.begin(), v.second.end(), []( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs.time.Val(); } ); + v.second.ensure_sorted(); } std::lock_guard lock( m_data.lock ); m_data.symbolSamplesReady = true; @@ -2321,11 +2321,15 @@ const Vector* Worker::GetSamplesForSymbol( uint64_t symAddr ) c return &it->second; } -const Vector* Worker::GetChildSamples( uint64_t addr ) const +const SortedVector* Worker::GetChildSamples( uint64_t addr ) { assert( m_data.symbolSamplesReady ); auto it = m_data.childSamples.find( addr ); if( it == m_data.childSamples.end() ) return nullptr; + // Out-of-order insertions may happen during live capture, e.g. when samples postponed + // due to missing context switch data are finally processed. Sorting is done lazily, as + // the vectors are only ever read here. + it->second.ensure_sorted(); return &it->second; } #endif @@ -6674,11 +6678,11 @@ void Worker::ProcessCallstackSampleImplStats( const SampleData& sd, ThreadData& auto it = m_data.childSamples.find( addr ); if( it == m_data.childSamples.end() ) { - m_data.childSamples.emplace( addr, Vector( ChildSample { sd.time, childAddr } ) ); + m_data.childSamples.emplace( addr, SortedVector( ChildSample { sd.time, childAddr } ) ); } else { - it->second.push_back_non_empty( ChildSample { sd.time, childAddr } ); + it->second.push_back( ChildSample { sd.time, childAddr } ); } childAddr = addr; } diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 3de937a3..22cd9410 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -342,7 +342,7 @@ private: unordered_flat_map> instructionPointersMap; unordered_flat_map> symbolSamples; unordered_flat_map, CallstackFrameIdHash, CallstackFrameIdCompare> pendingSymbolSamples; - unordered_flat_map> childSamples; + unordered_flat_map> childSamples; bool newFramesWereReceived = false; bool callstackSamplesReady = false; bool newContextSwitchesReceived = false; @@ -598,7 +598,7 @@ public: const VarArray& GetSyntheticCallstack( uint32_t idx ) const { return *m_data.syntheticCallstackPayload[idx]; } const CallstackFrameData* GetSyntheticCallstackFrame( const CallstackFrameId& ptr ) const; const Vector* GetSamplesForSymbol( uint64_t symAddr ) const; - const Vector* GetChildSamples( uint64_t addr ) const; + const SortedVector* GetChildSamples( uint64_t addr ); #endif const CrashEvent& GetCrashEvent() const { return m_data.crashEvent; }