From c4f42b1bc9c7cb125a4cebe50e8488bf45a1cabe Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Sat, 21 Mar 2026 01:26:40 +0100 Subject: [PATCH] Basic heuristics for reconstruction of zone callstack from sample data. --- profiler/src/profiler/TracyView.hpp | 2 + profiler/src/profiler/TracyView_Utility.cpp | 88 +++++++++++++++++++++ 2 files changed, 90 insertions(+) diff --git a/profiler/src/profiler/TracyView.hpp b/profiler/src/profiler/TracyView.hpp index 59feaf44..4931bab4 100644 --- a/profiler/src/profiler/TracyView.hpp +++ b/profiler/src/profiler/TracyView.hpp @@ -323,6 +323,8 @@ private: unordered_flat_map GetParentsCallstackFrameTreeTopDown( const unordered_flat_map& stacks, bool group ) const; void DrawParentsFrameTreeLevel( const unordered_flat_map& tree, int& idx ); + std::vector ReconstructZoneCallstack( const ZoneEvent& ev ) const; + void DrawInfoWindow(); void DrawZoneInfoWindow(); void DrawGpuInfoWindow(); diff --git a/profiler/src/profiler/TracyView_Utility.cpp b/profiler/src/profiler/TracyView_Utility.cpp index a7782486..9de6cdbc 100644 --- a/profiler/src/profiler/TracyView_Utility.cpp +++ b/profiler/src/profiler/TracyView_Utility.cpp @@ -998,4 +998,92 @@ nlohmann::json View::GetCallstackJson( const CallstackFrameId* data, size_t size return json; } +struct CallstackRoot +{ + unordered_flat_set stacks; + size_t maxLocalFrames; +}; + +std::vector View::ReconstructZoneCallstack( const ZoneEvent& ev ) const +{ + constexpr int SampleLimit = 10000; + std::vector ret; + + auto td = GetZoneThreadData( ev ); + if( !td ) return ret; + + auto it = std::lower_bound( td->samples.begin(), td->samples.end(), ev.Start(), [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } ); + auto end = std::lower_bound( it, td->samples.end(), m_worker.GetZoneEnd( ev ), [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } ); + if( std::distance( it, end ) > SampleLimit ) end = it + SampleLimit; + + unordered_flat_map roots; + while( it != end ) + { + auto stack = it->callstack.Val(); + auto& cs = m_worker.GetCallstack( stack ); + auto root = cs.back().data; + auto rit = roots.find( root ); + if( rit == roots.end() ) + { + roots.emplace( root, CallstackRoot { { stack } } ); + } + else + { + auto sit = rit->second.stacks.find( stack ); + if( sit == rit->second.stacks.end() ) rit->second.stacks.emplace( stack ); + } + ++it; + } + + auto rit = roots.begin(); + while( rit != roots.end() ) + { + size_t max = 0; + for( auto& stack : rit->second.stacks ) + { + size_t local = 0; + auto& cs = m_worker.GetCallstack( stack ); + auto sz = cs.size(); + for( int i = 0; i < sz; i++ ) + { + const auto& v = cs[i]; + const auto frameData = m_worker.GetCallstackFrame( v ); + if( !frameData ) break; + const auto& frame = frameData->data[frameData->size - 1]; + auto filename = m_worker.GetString( frame.file ); + auto image = frameData->imageName.Active() ? m_worker.GetString( frameData->imageName ) : nullptr; + if( !IsFrameExternal( filename, image ) ) local++; + } + max = std::max( max, local ); + } + + if( max > 0 ) + { + rit->second.maxLocalFrames = max; + ++rit; + } + else + { + rit = roots.erase( rit ); + } + } + if( roots.empty() ) return ret; + + auto maxElement = std::ranges::max_element( roots, [] ( const auto& l, const auto& r ) { return l.second.maxLocalFrames > r.second.maxLocalFrames; } ); + auto stacks = std::move( maxElement->second.stacks ); + roots.clear(); + + auto sit = stacks.begin(); + auto& scs = m_worker.GetCallstack( *sit ); + for( auto& v : scs ) ret.emplace_back( v ); + while( ++sit != stacks.end() ) + { + auto& cs = m_worker.GetCallstack( *sit ); + auto sz = cs.size(); + if( ret.size() > sz ) ret.erase( ret.begin(), ret.end() - sz ); + } + + return ret; +} + }