From 1dfcacd802cb859d36cafab433bf4986f9d46343 Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Sat, 25 Jul 2026 17:02:42 +0200 Subject: [PATCH] Collect entry stacks for "was this symbol reached" statistics. SymbolStats now has two additional entry stack maps, which record the call stacks below every occurrence of a symbol in a sample, not only when the symbol was at the top of the stack: - wasReached counts each occurrence separately. If a symbol re-enters itself through recursion, every re-entry adds an entry stack, which will itself contain the symbol somewhere below. - wasReachedNonReentrant counts only the outermost occurrence, so each sample contributes exactly once and recursion is ignored. The sum of counts in this map equals the symbol's inclusive sample count. To achieve this, the call stack is walked bottom-up, which makes the first encountered occurrence of a symbol the outermost one. Symbols at inline positions get the remaining inline frames of their frame group as a synthetic frame, mirroring what was already done for the top of the stack. The wasExecuting and wasExecutingBase maps are now filled by the same walk, as its topmost-frame special case. The keys they receive are identical to what the previous code produced. Note that there is no "base" variant of the reached maps. A base symbol is present as the last frame of every frame group that contains its inline functions, so its wasReached map already covers the whole symbol at base granularity. Merging in the inline symbols' maps, as done for wasExecutingBase, would only multi-count the same samples. --- server/TracyEvent.hpp | 2 ++ server/TracyWorker.cpp | 58 +++++++++++++++++++++++++++++------------- 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/server/TracyEvent.hpp b/server/TracyEvent.hpp index 1a5d264f..2bc39ce3 100644 --- a/server/TracyEvent.hpp +++ b/server/TracyEvent.hpp @@ -879,6 +879,8 @@ struct SymbolStats uint32_t incl, excl; unordered_flat_map wasExecuting; unordered_flat_map wasExecutingBase; + unordered_flat_map wasReached; + unordered_flat_map wasReachedNonReentrant; }; diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 602df680..25718b70 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -7853,25 +7853,49 @@ void Worker::UpdateSampleStatisticsImpl( const CallstackFrameData** frames, uint } }; - const bool topFrameHasInlines = fxsz != 1; - - CallstackFrameId parentFrameId; - if( topFrameHasInlines ) + // Walk the call stack bottom-up, so that the first occurrence of a symbol is the + // outermost one. Entry stacks are accumulated for each symbol on the way. Frames + // within a group are inline expansions, ordered innermost-first, hence the reverse + // iteration order. + seen.clear(); + auto suffixIdx = InternSyntheticCallstack( nullptr, cs, framesCount ); + for( int32_t c=framesCount-1; c>=0; c-- ) { - const CallstackFrameData cfd = { - .data = fexcl->data + 1, - .size = uint8_t( fxsz-1 ), - .imageName = fexcl->imageName - }; - parentFrameId = InternSyntheticFrame( cfd ); + const auto fdat = frames[c]; + const auto fsz = fdat->size; + for( int32_t f=fsz-1; f>=0; f-- ) + { + // suffixIdx contains the call stack below the current frame group. Symbols at + // inline positions also get the remaining inline frames of their own group, as + // a synthetic frame. + const auto symAddr = fdat->data[f].symAddr; + uint32_t idx; + if( f == fsz-1 ) + { + idx = suffixIdx; + } + else + { + const CallstackFrameData cfd = { + .data = fdat->data + f + 1, + .size = uint8_t( fsz - f - 1 ), + .imageName = fdat->imageName + }; + const auto synthetic = InternSyntheticFrame( cfd ); + idx = InternSyntheticCallstack( &synthetic, cs, c+1 ); + } + auto sym = m_data.symbolStats.find( symAddr ); + assert( sym != m_data.symbolStats.end() ); + UpdateEntry( sym->second.wasReached, idx, count ); + if( seen.emplace( symAddr ).second ) UpdateEntry( sym->second.wasReachedNonReentrant, idx, count ); + if( c == 0 && f == 0 ) + { + UpdateEntry( sym->second.wasExecuting, idx, count ); + UpdateEntry( sym->second.wasExecutingBase, suffixIdx, count ); + } + } + if( c > 0 ) suffixIdx = InternSyntheticCallstack( nullptr, cs, c ); } - - const auto parentIdx = InternSyntheticCallstack( topFrameHasInlines ? &parentFrameId : nullptr, cs, 1 ); - const auto baseParentIdx = InternSyntheticCallstack( nullptr, cs, 1 ); - - sym0 = m_data.symbolStats.find( frame0.symAddr ); - UpdateEntry( sym0->second.wasExecuting, parentIdx, count ); - UpdateEntry( sym0->second.wasExecutingBase, baseParentIdx, count ); } uint32_t Worker::InternSyntheticCallstack( const CallstackFrameId* head, const VarArray& cs, uint16_t from )