1
0
mirror of https://github.com/wolfpld/tracy.git synced 2025-03-20 07:40:02 +08:00

Merge pull request #230 from rokopt/recursive-zone-timings

Introduce "non-reentrant time" statistics view
This commit is contained in:
Bartosz Taudul 2021-06-26 21:17:45 +02:00 committed by GitHub
commit 9f185310be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 281 additions and 77 deletions

View File

@ -2820,7 +2820,7 @@ If the trace capture was performed with call stack sampling enabled (as describe
Here you will find a multi-column display of captured zones, which contains: the zone \emph{name} and \emph{location}, \emph{total time} spent in the zone, the \emph{count} of zone executions and the \emph{mean time spent in the zone per call}. The view may be sorted according to the three displayed values. Here you will find a multi-column display of captured zones, which contains: the zone \emph{name} and \emph{location}, \emph{total time} spent in the zone, the \emph{count} of zone executions and the \emph{mean time spent in the zone per call}. The view may be sorted according to the three displayed values.
The \emph{\faClock{}~Self time} option determines how the displayed time is calculated. If it is disabled, the measurements will be inclusive, that is, containing execution time of zone's children. Enabling the option switches the measurement to exclusive, displaying just the time spent in zone, subtracting the child calls. In the \emph{~Accumulation mode} menu, the \emph{~Including children} selection selection displays inclusive measurements, that is, containing execution time of zone's children. The \emph{~Self only} selection switches the measurement to exclusive, displaying just the time spent in zone, subtracting the child calls. The \emph{~Non-reentrant} selection displays inclusive time, but counting only the first appearance of a given zone on a thread's stack.
Clicking the \LMB{} left mouse button on a zone will open the individual zone statistics view in the find zone window (section~\ref{findzone}). Clicking the \LMB{} left mouse button on a zone will open the individual zone statistics view in the find zone window (section~\ref{findzone}).
@ -2847,7 +2847,7 @@ The \emph{Time} or \emph{Count} column (depending on the \emph{\faStopwatch{}~Sh
The last column, \emph{Code size}, displays the size of symbol in the executable image of the program. Since inlined routines are directly embedded into other functions, their symbol size will be based on the parent symbol, and displayed as 'less than'. In some cases this data won't be available. If the symbol code has been retrieved\footnote{Symbols larger than 64~KB are not captured.}, symbol size will be prepend with the \texttt{\faDatabase}~icon, and clicking the \RMB{}~right mouse button on the location column entry will open symbol view window (section~\ref{symbolview}). The last column, \emph{Code size}, displays the size of symbol in the executable image of the program. Since inlined routines are directly embedded into other functions, their symbol size will be based on the parent symbol, and displayed as 'less than'. In some cases this data won't be available. If the symbol code has been retrieved\footnote{Symbols larger than 64~KB are not captured.}, symbol size will be prepend with the \texttt{\faDatabase}~icon, and clicking the \RMB{}~right mouse button on the location column entry will open symbol view window (section~\ref{symbolview}).
Finally, the list can be filtered using the \emph{\faFilter{}~Filter symbols} entry field, just like in the instrumentation mode case. Additionally, you can also filter results by the originating image name of the symbol. Display of kernel symbols may be disabled with the \emph{\faHatWizard{}~Include kernel} switch. The exclusive/inclusive time counting mode can be switched using the \emph{\faClock{}~Self time} switch. Limiting the time range is also available, but is restricted to self time. If the \emph{\faPuzzlePiece{}~Show all} option is selected, the list will include not only call stack samples, but also all other symbols collected during the profiling process (this is enabled by default, if no sampling was performed). Finally, the list can be filtered using the \emph{\faFilter{}~Filter symbols} entry field, just like in the instrumentation mode case. Additionally, you can also filter results by the originating image name of the symbol. Display of kernel symbols may be disabled with the \emph{\faHatWizard{}~Include kernel} switch. The exclusive/inclusive/non-reentrant time counting mode can be switched using the \emph{~Accumulation mode} menu. Limiting the time range is also available, but is restricted to self time. If the \emph{\faPuzzlePiece{}~Show all} option is selected, the list will include not only call stack samples, but also all other symbols collected during the profiling process (this is enabled by default, if no sampling was performed).
\subsection{Find zone window} \subsection{Find zone window}
\label{findzone} \label{findzone}

View File

@ -587,12 +587,57 @@ enum { GhostZoneSize = sizeof( GhostZone ) };
#pragma pack() #pragma pack()
using SrcLocCountMap = unordered_flat_map<int16_t, size_t>;
static tracy_force_inline void IncSrcLocCount( SrcLocCountMap& countMap, int16_t srcloc )
{
const auto it = countMap.find( srcloc );
if( it == countMap.end() )
{
countMap.emplace( srcloc, 1 );
return;
}
assert( it->second != 0 );
it->second++;
}
static tracy_force_inline bool DecSrcLocCount( SrcLocCountMap& countMap, int16_t srcloc )
{
const auto it = countMap.find( srcloc );
assert( it != countMap.end() );
assert( it->second != 0 );
if( it->second == 1 )
{
countMap.erase( it );
return false;
}
it->second--;
return true;
}
static tracy_force_inline bool HasSrcLocCount( const SrcLocCountMap& countMap, int16_t srcloc )
{
const auto it = countMap.find( srcloc );
if( it != countMap.end() )
{
assert( it->second != 0 );
return true;
}
return false;
}
struct ThreadData struct ThreadData
{ {
uint64_t id; uint64_t id;
uint64_t count; uint64_t count;
Vector<short_ptr<ZoneEvent>> timeline; Vector<short_ptr<ZoneEvent>> timeline;
Vector<short_ptr<ZoneEvent>> stack; Vector<short_ptr<ZoneEvent>> stack;
SrcLocCountMap stackCount;
Vector<short_ptr<MessageData>> messages; Vector<short_ptr<MessageData>> messages;
uint32_t nextZoneId; uint32_t nextZoneId;
Vector<uint32_t> zoneIdStack; Vector<uint32_t> zoneIdStack;
@ -604,6 +649,9 @@ struct ThreadData
Vector<SampleData> samples; Vector<SampleData> samples;
SampleData pendingSample; SampleData pendingSample;
uint64_t kernelSampleCnt; uint64_t kernelSampleCnt;
tracy_force_inline void IncStackCount( int16_t srcloc ) { IncSrcLocCount( stackCount, srcloc ); }
tracy_force_inline bool DecStackCount( int16_t srcloc ) { return DecSrcLocCount( stackCount, srcloc ); }
}; };
struct GpuCtxThreadData struct GpuCtxThreadData

View File

@ -12269,9 +12269,23 @@ struct SrcLocZonesSlim
int16_t srcloc; int16_t srcloc;
size_t numZones; size_t numZones;
int64_t total; int64_t total;
int64_t selfTotal;
}; };
void View::AccumulationModeComboBox()
{
ImGui::TextUnformatted( "Timing" );
ImGui::SameLine();
const char* accumulationModeTable = m_statMode == 0 ? "Self only\0With children\0Non-reentrant\0" : "Self only\0With children\0";
ImGui::SetNextItemWidth( ImGui::CalcTextSize( "Non-reentrant" ).x + ImGui::GetTextLineHeight() * 2 );
if ( m_statMode != 0 && m_statAccumulationMode == AccumulationMode::NonReentrantChildren )
{
m_statAccumulationMode = AccumulationMode::SelfOnly;
}
int accumulationMode = static_cast<int>( m_statAccumulationMode );
ImGui::Combo( "##accumulationMode", &accumulationMode, accumulationModeTable );
m_statAccumulationMode = static_cast<AccumulationMode>( accumulationMode );
}
void View::DrawStatistics() void View::DrawStatistics()
{ {
ImGui::SetNextWindowSize( ImVec2( 1400, 600 ), ImGuiCond_FirstUseEver ); ImGui::SetNextWindowSize( ImVec2( 1400, 600 ), ImGuiCond_FirstUseEver );
@ -12302,10 +12316,14 @@ void View::DrawStatistics()
if( hasSamples ) if( hasSamples )
{ {
ImGui::Spacing();
ImGui::SameLine();
ImGui::RadioButton( ICON_FA_EYE_DROPPER " Sampling", &m_statMode, 1 ); ImGui::RadioButton( ICON_FA_EYE_DROPPER " Sampling", &m_statMode, 1 );
} }
else else
{ {
ImGui::Spacing();
ImGui::SameLine();
ImGui::RadioButton( ICON_FA_PUZZLE_PIECE " Symbols", &m_statMode, 1 ); ImGui::RadioButton( ICON_FA_PUZZLE_PIECE " Symbols", &m_statMode, 1 );
} }
ImGui::SameLine(); ImGui::SameLine();
@ -12349,19 +12367,18 @@ void View::DrawStatistics()
if( !filterActive ) if( !filterActive )
{ {
auto cit = m_statCache.find( it->first ); auto cit = m_statCache.find( it->first );
if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.sourceCount == it->second.zones.size() ) if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
{ {
if( cit->second.count != 0 ) if( cit->second.count != 0 )
{ {
slzcnt++; slzcnt++;
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total, cit->second.selfTotal } ); srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
} }
} }
else else
{ {
size_t cnt = 0; size_t cnt = 0;
int64_t total = 0; int64_t total = 0;
int64_t selfTotal = 0;
for( auto& v : it->second.zones ) for( auto& v : it->second.zones )
{ {
auto& z = *v.Zone(); auto& z = *v.Zone();
@ -12370,17 +12387,24 @@ void View::DrawStatistics()
if( start >= min && end <= max ) if( start >= min && end <= max )
{ {
const auto zt = end - start; const auto zt = end - start;
total += zt; if ( m_statAccumulationMode == AccumulationMode::SelfOnly)
if( m_statSelf ) selfTotal += zt - GetZoneChildTimeFast( z ); {
cnt++; total += zt - GetZoneChildTimeFast( z );
cnt++;
}
else if ( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry(z) )
{
total += zt;
cnt++;
}
} }
} }
if( cnt != 0 ) if( cnt != 0 )
{ {
slzcnt++; slzcnt++;
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total, selfTotal } ); srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
} }
m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, it->second.zones.size(), cnt, total, selfTotal }; m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
} }
} }
else else
@ -12391,18 +12415,17 @@ void View::DrawStatistics()
if( m_statisticsFilter.PassFilter( name ) ) if( m_statisticsFilter.PassFilter( name ) )
{ {
auto cit = m_statCache.find( it->first ); auto cit = m_statCache.find( it->first );
if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.sourceCount == it->second.zones.size() ) if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
{ {
if( cit->second.count != 0 ) if( cit->second.count != 0 )
{ {
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total, cit->second.selfTotal } ); srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
} }
} }
else else
{ {
size_t cnt = 0; size_t cnt = 0;
int64_t total = 0; int64_t total = 0;
int64_t selfTotal = 0;
for( auto& v : it->second.zones ) for( auto& v : it->second.zones )
{ {
auto& z = *v.Zone(); auto& z = *v.Zone();
@ -12411,16 +12434,23 @@ void View::DrawStatistics()
if( start >= min && end <= max ) if( start >= min && end <= max )
{ {
const auto zt = end - start; const auto zt = end - start;
total += zt; if ( m_statAccumulationMode == AccumulationMode::SelfOnly)
if( m_statSelf ) selfTotal += zt - GetZoneChildTimeFast( z ); {
cnt++; total += zt - GetZoneChildTimeFast( z );
cnt++;
}
else if ( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry(z) )
{
total += zt;
cnt++;
}
} }
} }
if( cnt != 0 ) if( cnt != 0 )
{ {
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total, selfTotal } ); srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
} }
m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, it->second.zones.size(), cnt, total, selfTotal }; m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
} }
} }
} }
@ -12434,9 +12464,26 @@ void View::DrawStatistics()
if( it->second.total != 0 ) if( it->second.total != 0 )
{ {
slzcnt++; slzcnt++;
size_t count;
int64_t total;
switch( m_statAccumulationMode )
{
case AccumulationMode::SelfOnly:
count = it->second.zones.size();
total = it->second.selfTotal;
break;
case AccumulationMode::AllChildren:
count = it->second.zones.size();
total = it->second.total;
break;
case AccumulationMode::NonReentrantChildren:
count = it->second.nonReentrantCount;
total = it->second.nonReentrantTotal;
break;
}
if( !filterActive ) if( !filterActive )
{ {
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, it->second.zones.size(), it->second.total, it->second.selfTotal } ); srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
} }
else else
{ {
@ -12444,7 +12491,7 @@ void View::DrawStatistics()
auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function ); auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
if( m_statisticsFilter.PassFilter( name ) ) if( m_statisticsFilter.PassFilter( name ) )
{ {
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, it->second.zones.size(), it->second.total, it->second.selfTotal } ); srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
} }
} }
} }
@ -12459,7 +12506,7 @@ void View::DrawStatistics()
ImGui::SameLine(); ImGui::SameLine();
ImGui::Spacing(); ImGui::Spacing();
ImGui::SameLine(); ImGui::SameLine();
ImGui::Checkbox( ICON_FA_CLOCK " Self time", &m_statSelf ); AccumulationModeComboBox();
} }
else else
{ {
@ -12469,7 +12516,7 @@ void View::DrawStatistics()
{ {
ImGui::PushItemFlag( ImGuiItemFlags_Disabled, true ); ImGui::PushItemFlag( ImGuiItemFlags_Disabled, true );
ImGui::PushStyleVar( ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f ); ImGui::PushStyleVar( ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f );
m_statSelf = true; m_statAccumulationMode = AccumulationMode::SelfOnly;
bool val = true; bool val = true;
ImGui::Checkbox( ICON_FA_CLOCK " Self time", &val ); ImGui::Checkbox( ICON_FA_CLOCK " Self time", &val );
ImGui::PopItemFlag(); ImGui::PopItemFlag();
@ -12483,17 +12530,30 @@ void View::DrawStatistics()
} }
else else
{ {
ImGui::Checkbox( ICON_FA_CLOCK " Self time", &m_statSelf ); ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
AccumulationModeComboBox();
} }
ImGui::SameLine(); ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_EYE_SLASH " Hide unknown", &m_statHideUnknown ); ImGui::Checkbox( ICON_FA_EYE_SLASH " Hide unknown", &m_statHideUnknown );
ImGui::SameLine(); ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_PUZZLE_PIECE " Show all", &m_showAllSymbols ); ImGui::Checkbox( ICON_FA_PUZZLE_PIECE " Show all", &m_showAllSymbols );
ImGui::SameLine(); ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_SITEMAP " Inlines", &m_statSeparateInlines ); ImGui::Checkbox( ICON_FA_SITEMAP " Inlines", &m_statSeparateInlines );
ImGui::SameLine(); ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::Checkbox( ICON_FA_AT " Address", &m_statShowAddress ); ImGui::Checkbox( ICON_FA_AT " Address", &m_statShowAddress );
ImGui::SameLine(); ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
ImGui::TextUnformatted( "Location:" ); ImGui::TextUnformatted( "Location:" );
ImGui::SameLine(); ImGui::SameLine();
const char* locationTable = "Entry\0Sample\0Smart\0"; const char* locationTable = "Entry\0Sample\0Smart\0";
@ -12614,27 +12674,13 @@ void View::DrawStatistics()
} }
break; break;
case 2: case 2:
if( m_statSelf ) if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{ {
if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total < rhs.total; } );
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.selfTotal < rhs.selfTotal; } );
}
else
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.selfTotal > rhs.selfTotal; } );
}
} }
else else
{ {
if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total > rhs.total; } );
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total < rhs.total; } );
}
else
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total > rhs.total; } );
}
} }
break; break;
case 3: case 3:
@ -12648,27 +12694,13 @@ void View::DrawStatistics()
} }
break; break;
case 4: case 4:
if( m_statSelf ) if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{ {
if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones < rhs.total / rhs.numZones; } );
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.selfTotal / lhs.numZones < rhs.selfTotal / rhs.numZones; } );
}
else
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.selfTotal / lhs.numZones > rhs.selfTotal / rhs.numZones; } );
}
} }
else else
{ {
if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones > rhs.total / rhs.numZones; } );
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones < rhs.total / rhs.numZones; } );
}
else
{
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones > rhs.total / rhs.numZones; } );
}
} }
break; break;
default: default:
@ -12721,7 +12753,7 @@ void View::DrawStatistics()
ImGui::Unindent( indentVal ); ImGui::Unindent( indentVal );
} }
ImGui::TableNextColumn(); ImGui::TableNextColumn();
const auto time = m_statSelf ? v.selfTotal : v.total; const auto time = v.total;
ImGui::TextUnformatted( TimeToString( time ) ); ImGui::TextUnformatted( TimeToString( time ) );
ImGui::SameLine(); ImGui::SameLine();
char buf[64]; char buf[64];
@ -12730,7 +12762,7 @@ void View::DrawStatistics()
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TextUnformatted( RealToString( v.numZones ) ); ImGui::TextUnformatted( RealToString( v.numZones ) );
ImGui::TableNextColumn(); ImGui::TableNextColumn();
ImGui::TextUnformatted( TimeToString( ( m_statSelf ? v.selfTotal : v.total ) / v.numZones ) ); ImGui::TextUnformatted( TimeToString( time / v.numZones ) );
ImGui::PopID(); ImGui::PopID();
} }
ImGui::EndTable(); ImGui::EndTable();
@ -12971,7 +13003,7 @@ void View::DrawStatistics()
} }
else else
{ {
if( m_statSelf ) if( m_statAccumulationMode == AccumulationMode::SelfOnly )
{ {
pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l.excl != r.excl ? l.excl > r.excl : l.symAddr < r.symAddr; } ); pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l.excl != r.excl ? l.excl > r.excl : l.symAddr < r.symAddr; } );
} }
@ -13008,7 +13040,7 @@ void View::DrawStatistics()
int idx = 0; int idx = 0;
for( auto& v : data ) for( auto& v : data )
{ {
const auto cnt = m_statSelf ? v.excl : v.incl; const auto cnt = m_statAccumulationMode == AccumulationMode::SelfOnly ? v.excl : v.incl;
if( cnt > 0 || showAll ) if( cnt > 0 || showAll )
{ {
const char* name = "[unknown]"; const char* name = "[unknown]";
@ -13247,7 +13279,7 @@ void View::DrawStatistics()
inSymList.push_back( SymList { v.symAddr, statIt->second.incl, statIt->second.excl } ); inSymList.push_back( SymList { v.symAddr, statIt->second.incl, statIt->second.excl } );
} }
if( m_statSelf ) if( m_statAccumulationMode == AccumulationMode::SelfOnly )
{ {
pdqsort_branchless( inSymList.begin(), inSymList.end(), []( const auto& l, const auto& r ) { return l.excl != r.excl ? l.excl > r.excl : l.symAddr < r.symAddr; } ); pdqsort_branchless( inSymList.begin(), inSymList.end(), []( const auto& l, const auto& r ) { return l.excl != r.excl ? l.excl > r.excl : l.symAddr < r.symAddr; } );
} }
@ -13261,7 +13293,7 @@ void View::DrawStatistics()
{ {
ImGui::TableNextRow(); ImGui::TableNextRow();
ImGui::TableNextColumn(); ImGui::TableNextColumn();
const auto cnt = m_statSelf ? iv.excl : iv.incl; const auto cnt = m_statAccumulationMode == AccumulationMode::SelfOnly ? iv.excl : iv.incl;
if( cnt > 0 || showAll ) if( cnt > 0 || showAll )
{ {
auto sit = symMap.find( iv.symAddr ); auto sit = symMap.find( iv.symAddr );
@ -17523,6 +17555,93 @@ const ZoneEvent* View::GetZoneParent( const ZoneEvent& zone, uint64_t tid ) cons
return nullptr; return nullptr;
} }
bool View::IsZoneReentry( const ZoneEvent& zone ) const
{
#ifndef TRACY_NO_STATISTICS
if( m_worker.AreSourceLocationZonesReady() )
{
auto& slz = m_worker.GetZonesForSourceLocation( zone.SrcLoc() );
if( !slz.zones.empty() )
{
auto it = std::lower_bound( slz.zones.begin(), slz.zones.end(), zone.Start(), [] ( const auto& lhs, const auto& rhs ) { return lhs.Zone()->Start() < rhs; } );
if( it != slz.zones.end() && it->Zone() == &zone )
{
return IsZoneReentry( zone, m_worker.DecompressThread( it->Thread() ) );
}
}
}
#endif
for( const auto& thread : m_worker.GetThreadData() )
{
const ZoneEvent* parent = nullptr;
const Vector<short_ptr<ZoneEvent>>* timeline = &thread->timeline;
if( timeline->empty() ) continue;
for(;;)
{
if( timeline->is_magic() )
{
auto vec = (Vector<ZoneEvent>*)timeline;
auto it = std::upper_bound( vec->begin(), vec->end(), zone.Start(), [] ( const auto& l, const auto& r ) { return l < r.Start(); } );
if( it != vec->begin() ) --it;
if( zone.IsEndValid() && it->Start() > zone.End() ) break;
if( it == &zone ) return false;
if( !it->HasChildren() ) break;
parent = it;
if (parent->SrcLoc() == zone.SrcLoc() ) return true;
timeline = &m_worker.GetZoneChildren( parent->Child() );
}
else
{
auto it = std::upper_bound( timeline->begin(), timeline->end(), zone.Start(), [] ( const auto& l, const auto& r ) { return l < r->Start(); } );
if( it != timeline->begin() ) --it;
if( zone.IsEndValid() && (*it)->Start() > zone.End() ) break;
if( *it == &zone ) return false;
if( !(*it)->HasChildren() ) break;
parent = *it;
if (parent->SrcLoc() == zone.SrcLoc() ) return true;
timeline = &m_worker.GetZoneChildren( parent->Child() );
}
}
}
return false;
}
bool View::IsZoneReentry( const ZoneEvent& zone, uint64_t tid ) const
{
const auto thread = m_worker.GetThreadData( tid );
const ZoneEvent* parent = nullptr;
const Vector<short_ptr<ZoneEvent>>* timeline = &thread->timeline;
if( timeline->empty() ) return false;
for(;;)
{
if( timeline->is_magic() )
{
auto vec = (Vector<ZoneEvent>*)timeline;
auto it = std::upper_bound( vec->begin(), vec->end(), zone.Start(), [] ( const auto& l, const auto& r ) { return l < r.Start(); } );
if( it != vec->begin() ) --it;
if( zone.IsEndValid() && it->Start() > zone.End() ) break;
if( it == &zone ) return false;
if( !it->HasChildren() ) break;
parent = it;
if (parent->SrcLoc() == zone.SrcLoc() ) return true;
timeline = &m_worker.GetZoneChildren( parent->Child() );
}
else
{
auto it = std::upper_bound( timeline->begin(), timeline->end(), zone.Start(), [] ( const auto& l, const auto& r ) { return l < r->Start(); } );
if( it != timeline->begin() ) --it;
if( zone.IsEndValid() && (*it)->Start() > zone.End() ) break;
if( *it == &zone ) return false;
if( !(*it)->HasChildren() ) break;
parent = *it;
if (parent->SrcLoc() == zone.SrcLoc() ) return true;
timeline = &m_worker.GetZoneChildren( parent->Child() );
}
}
return false;
}
const GpuEvent* View::GetZoneParent( const GpuEvent& zone ) const const GpuEvent* View::GetZoneParent( const GpuEvent& zone ) const
{ {
for( const auto& ctx : m_worker.GetGpuData() ) for( const auto& ctx : m_worker.GetGpuData() )

View File

@ -56,13 +56,20 @@ class View
uint64_t count; uint64_t count;
}; };
enum class AccumulationMode
{
SelfOnly,
AllChildren,
NonReentrantChildren
};
struct StatisticsCache struct StatisticsCache
{ {
RangeSlim range; RangeSlim range;
AccumulationMode accumulationMode;
size_t sourceCount; size_t sourceCount;
size_t count; size_t count;
int64_t total; int64_t total;
int64_t selfTotal;
}; };
public: public:
@ -187,6 +194,7 @@ private:
void DrawMessages(); void DrawMessages();
void DrawMessageLine( const MessageData& msg, bool hasCallstack, int& idx ); void DrawMessageLine( const MessageData& msg, bool hasCallstack, int& idx );
void DrawFindZone(); void DrawFindZone();
void AccumulationModeComboBox();
void DrawStatistics(); void DrawStatistics();
void DrawMemory(); void DrawMemory();
void DrawAllocList(); void DrawAllocList();
@ -250,6 +258,8 @@ private:
const ZoneEvent* GetZoneParent( const ZoneEvent& zone ) const; const ZoneEvent* GetZoneParent( const ZoneEvent& zone ) const;
const ZoneEvent* GetZoneParent( const ZoneEvent& zone, uint64_t tid ) const; const ZoneEvent* GetZoneParent( const ZoneEvent& zone, uint64_t tid ) const;
bool IsZoneReentry( const ZoneEvent& zone ) const;
bool IsZoneReentry( const ZoneEvent& zone, uint64_t tid ) const;
const GpuEvent* GetZoneParent( const GpuEvent& zone ) const; const GpuEvent* GetZoneParent( const GpuEvent& zone ) const;
const ThreadData* GetZoneThreadData( const ZoneEvent& zone ) const; const ThreadData* GetZoneThreadData( const ZoneEvent& zone ) const;
uint64_t GetZoneThread( const ZoneEvent& zone ) const; uint64_t GetZoneThread( const ZoneEvent& zone ) const;
@ -392,7 +402,7 @@ private:
bool m_showCpuDataWindow = false; bool m_showCpuDataWindow = false;
bool m_showAnnotationList = false; bool m_showAnnotationList = false;
bool m_statSelf = true; AccumulationMode m_statAccumulationMode = AccumulationMode::SelfOnly;
bool m_statSampleTime = true; bool m_statSampleTime = true;
int m_statMode = 0; int m_statMode = 0;
int m_statSampleLocation = 2; int m_statSampleLocation = 2;

View File

@ -389,11 +389,11 @@ Worker::Worker( const char* name, const char* program, const std::vector<ImportE
else else
{ {
auto td = NoticeThread( v.tid ); auto td = NoticeThread( v.tid );
if (td->zoneIdStack.empty()) if( td->zoneIdStack.empty() ) continue;
continue;
td->zoneIdStack.pop_back(); td->zoneIdStack.pop_back();
auto& stack = td->stack; auto& stack = td->stack;
auto zone = stack.back_and_pop(); auto zone = stack.back_and_pop();
td->DecStackCount( zone->SrcLoc() );
zone->SetEnd( v.timestamp ); zone->SetEnd( v.timestamp );
#ifndef TRACY_NO_STATISTICS #ifndef TRACY_NO_STATISTICS
@ -564,8 +564,7 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
char tmp[1024]; char tmp[1024];
f.Read( tmp, sz ); f.Read( tmp, sz );
m_captureName = std::string( tmp, tmp+sz ); m_captureName = std::string( tmp, tmp+sz );
if (m_captureName.empty()) if( m_captureName.empty() ) m_captureName = f.GetFilename();
m_captureName = f.GetFilename();
} }
{ {
f.Read( sz ); f.Read( sz );
@ -1819,16 +1818,21 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
if( mem.second->reconstruct ) jobs.emplace_back( std::thread( [this, mem = mem.second] { ReconstructMemAllocPlot( *mem ); } ) ); if( mem.second->reconstruct ) jobs.emplace_back( std::thread( [this, mem = mem.second] { ReconstructMemAllocPlot( *mem ); } ) );
} }
std::function<void(Vector<short_ptr<ZoneEvent>>&, uint16_t)> ProcessTimeline; std::function<void(SrcLocCountMap&, Vector<short_ptr<ZoneEvent>>&, uint16_t)> ProcessTimeline;
ProcessTimeline = [this, &ProcessTimeline] ( Vector<short_ptr<ZoneEvent>>& _vec, uint16_t thread ) ProcessTimeline = [this, &ProcessTimeline] ( SrcLocCountMap& countMap, Vector<short_ptr<ZoneEvent>>& _vec, uint16_t thread )
{ {
if( m_shutdown.load( std::memory_order_relaxed ) ) return; if( m_shutdown.load( std::memory_order_relaxed ) ) return;
assert( _vec.is_magic() ); assert( _vec.is_magic() );
auto& vec = *(Vector<ZoneEvent>*)( &_vec ); auto& vec = *(Vector<ZoneEvent>*)( &_vec );
for( auto& zone : vec ) for( auto& zone : vec )
{ {
if( zone.IsEndValid() ) ReconstructZoneStatistics( zone, thread ); if( zone.IsEndValid() ) ReconstructZoneStatistics( countMap, zone, thread );
if( zone.HasChildren() ) ProcessTimeline( GetZoneChildrenMutable( zone.Child() ), thread ); if( zone.HasChildren() )
{
IncSrcLocCount( countMap, zone.SrcLoc() );
ProcessTimeline( countMap, GetZoneChildrenMutable( zone.Child() ), thread );
DecSrcLocCount( countMap, zone.SrcLoc() );
}
} }
}; };
@ -1838,8 +1842,9 @@ Worker::Worker( FileRead& f, EventType::Type eventMask, bool bgTasks )
if( m_shutdown.load( std::memory_order_relaxed ) ) return; if( m_shutdown.load( std::memory_order_relaxed ) ) return;
if( !t->timeline.empty() ) if( !t->timeline.empty() )
{ {
SrcLocCountMap countMap;
// Don't touch thread compression cache in a thread. // Don't touch thread compression cache in a thread.
ProcessTimeline( t->timeline, m_data.localThreadCompress.DecompressMustRaw( t->id ) ); ProcessTimeline( countMap, t->timeline, m_data.localThreadCompress.DecompressMustRaw( t->id ) );
} }
} }
} ) ); } ) );
@ -2006,6 +2011,7 @@ Worker::~Worker()
{ {
v->timeline.~Vector(); v->timeline.~Vector();
v->stack.~Vector(); v->stack.~Vector();
v->stackCount.~Table();
v->messages.~Vector(); v->messages.~Vector();
v->zoneIdStack.~Vector(); v->zoneIdStack.~Vector();
v->samples.~Vector(); v->samples.~Vector();
@ -3581,6 +3587,7 @@ void Worker::NewZone( ZoneEvent* zone, uint64_t thread )
auto td = m_threadCtxData; auto td = m_threadCtxData;
if( !td ) td = m_threadCtxData = NoticeThread( thread ); if( !td ) td = m_threadCtxData = NoticeThread( thread );
td->count++; td->count++;
td->IncStackCount( zone->SrcLoc() );
const auto ssz = td->stack.size(); const auto ssz = td->stack.size();
if( ssz == 0 ) if( ssz == 0 )
{ {
@ -4735,6 +4742,7 @@ void Worker::ProcessZoneEnd( const QueueZoneEnd& ev )
assert( !stack.empty() ); assert( !stack.empty() );
auto zone = stack.back_and_pop(); auto zone = stack.back_and_pop();
assert( zone->End() == -1 ); assert( zone->End() == -1 );
const auto isReentry = td->DecStackCount( zone->SrcLoc() );
const auto refTime = m_refTimeThread + ev.time; const auto refTime = m_refTimeThread + ev.time;
m_refTimeThread = refTime; m_refTimeThread = refTime;
const auto timeEnd = TscTime( refTime - m_data.baseTime ); const auto timeEnd = TscTime( refTime - m_data.baseTime );
@ -4788,6 +4796,13 @@ void Worker::ProcessZoneEnd( const QueueZoneEnd& ev )
if( slz->selfMin > selfSpan ) slz->selfMin = selfSpan; if( slz->selfMin > selfSpan ) slz->selfMin = selfSpan;
if( slz->selfMax < selfSpan ) slz->selfMax = selfSpan; if( slz->selfMax < selfSpan ) slz->selfMax = selfSpan;
slz->selfTotal += selfSpan; slz->selfTotal += selfSpan;
if( !isReentry )
{
slz->nonReentrantCount++;
if( slz->nonReentrantMin > timeSpan ) slz->nonReentrantMin = timeSpan;
if( slz->nonReentrantMax < timeSpan ) slz->nonReentrantMax = timeSpan;
slz->nonReentrantTotal += timeSpan;
}
if( !td->childTimeStack.empty() ) if( !td->childTimeStack.empty() )
{ {
td->childTimeStack.back() += timeSpan; td->childTimeStack.back() += timeSpan;
@ -7041,7 +7056,7 @@ void Worker::ReadTimelineHaveSize( FileRead& f, GpuEvent* zone, int64_t& refTime
} }
#ifndef TRACY_NO_STATISTICS #ifndef TRACY_NO_STATISTICS
void Worker::ReconstructZoneStatistics( ZoneEvent& zone, uint16_t thread ) void Worker::ReconstructZoneStatistics( SrcLocCountMap& countMap, ZoneEvent& zone, uint16_t thread )
{ {
assert( zone.IsEndValid() ); assert( zone.IsEndValid() );
auto timeSpan = zone.End() - zone.Start(); auto timeSpan = zone.End() - zone.Start();
@ -7058,6 +7073,14 @@ void Worker::ReconstructZoneStatistics( ZoneEvent& zone, uint16_t thread )
if( slz.max < timeSpan ) slz.max = timeSpan; if( slz.max < timeSpan ) slz.max = timeSpan;
slz.total += timeSpan; slz.total += timeSpan;
slz.sumSq += double( timeSpan ) * timeSpan; slz.sumSq += double( timeSpan ) * timeSpan;
const auto isReentry = HasSrcLocCount( countMap, zone.SrcLoc() );
if( !isReentry )
{
slz.nonReentrantCount++;
if( slz.nonReentrantMin > timeSpan ) slz.nonReentrantMin = timeSpan;
if( slz.nonReentrantMax < timeSpan ) slz.nonReentrantMax = timeSpan;
slz.nonReentrantTotal += timeSpan;
}
if( zone.HasChildren() ) if( zone.HasChildren() )
{ {
auto& children = GetZoneChildren( zone.Child() ); auto& children = GetZoneChildren( zone.Child() );

View File

@ -184,6 +184,10 @@ private:
int64_t selfMin = std::numeric_limits<int64_t>::max(); int64_t selfMin = std::numeric_limits<int64_t>::max();
int64_t selfMax = std::numeric_limits<int64_t>::min(); int64_t selfMax = std::numeric_limits<int64_t>::min();
int64_t selfTotal = 0; int64_t selfTotal = 0;
size_t nonReentrantCount = 0;
int64_t nonReentrantMin = std::numeric_limits<int64_t>::max();
int64_t nonReentrantMax = std::numeric_limits<int64_t>::min();
int64_t nonReentrantTotal = 0;
}; };
struct CallstackFrameIdHash struct CallstackFrameIdHash
@ -831,7 +835,7 @@ private:
tracy_force_inline void ReadTimelineHaveSize( FileRead& f, GpuEvent* zone, int64_t& refTime, int64_t& refGpuTime, int32_t& childIdx, uint64_t sz ); tracy_force_inline void ReadTimelineHaveSize( FileRead& f, GpuEvent* zone, int64_t& refTime, int64_t& refGpuTime, int32_t& childIdx, uint64_t sz );
#ifndef TRACY_NO_STATISTICS #ifndef TRACY_NO_STATISTICS
tracy_force_inline void ReconstructZoneStatistics( ZoneEvent& zone, uint16_t thread ); tracy_force_inline void ReconstructZoneStatistics( SrcLocCountMap& countMap, ZoneEvent& zone, uint16_t thread );
#else #else
tracy_force_inline void CountZoneStatistics( ZoneEvent* zone ); tracy_force_inline void CountZoneStatistics( ZoneEvent* zone );
#endif #endif