mirror of
https://github.com/wolfpld/tracy.git
synced 2024-11-22 22:44:34 +00:00
51c99ba3c2
This copies the visible rows of either the instrumentation or GPU statistics view to a CSV string matching a subset of the csvexport format.
947 lines
43 KiB
C++
947 lines
43 KiB
C++
#include <sstream>
|
|
|
|
#include "TracyFilesystem.hpp"
|
|
#include "TracyImGui.hpp"
|
|
#include "TracyPrint.hpp"
|
|
#include "TracyView.hpp"
|
|
|
|
namespace tracy
|
|
{
|
|
|
|
extern double s_time;
|
|
|
|
struct SrcLocZonesSlim
|
|
{
|
|
int16_t srcloc;
|
|
uint16_t numThreads;
|
|
size_t numZones;
|
|
int64_t total;
|
|
};
|
|
|
|
void View::AccumulationModeComboBox()
|
|
{
|
|
ImGui::TextUnformatted( "Timing" );
|
|
ImGui::SameLine();
|
|
const char* accumulationModeTable = m_statMode == 1 ? "Self only\0With children\0" : "Self only\0With children\0Non-reentrant\0";
|
|
ImGui::SetNextItemWidth( ImGui::CalcTextSize( "Non-reentrant" ).x + ImGui::GetTextLineHeight() * 2 );
|
|
if( m_statMode == 1 && 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()
|
|
{
|
|
const auto scale = GetScale();
|
|
ImGui::SetNextWindowSize( ImVec2( 1400 * scale, 600 * scale ), ImGuiCond_FirstUseEver );
|
|
ImGui::Begin( "Statistics", &m_showStatistics, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
|
|
if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; }
|
|
#ifdef TRACY_NO_STATISTICS
|
|
ImGui::TextWrapped( "Collection of statistical data is disabled in this build." );
|
|
ImGui::TextWrapped( "Rebuild without the TRACY_NO_STATISTICS macro to enable statistics view." );
|
|
#else
|
|
if( !m_worker.AreSourceLocationZonesReady() && ( !m_worker.AreCallstackSamplesReady() || m_worker.GetCallstackSampleCount() == 0 ) )
|
|
{
|
|
ImGui::TextWrapped( "Please wait, computing data..." );
|
|
DrawWaitingDots( s_time );
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 2, 2 ) );
|
|
ImGui::RadioButton( ICON_FA_SYRINGE " Instrumentation", &m_statMode, 0 );
|
|
if( m_worker.AreCallstackSamplesReady() )
|
|
{
|
|
ImGui::SameLine();
|
|
if( m_worker.GetCallstackSampleCount() > 0 )
|
|
{
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton( ICON_FA_EYE_DROPPER " Sampling", &m_statMode, 1 );
|
|
}
|
|
else if( m_worker.GetSymbolsCount() > 0 )
|
|
{
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton( ICON_FA_PUZZLE_PIECE " Symbols", &m_statMode, 1 );
|
|
}
|
|
}
|
|
if( m_worker.GetGpuZoneCount() > 0 )
|
|
{
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::RadioButton( ICON_FA_EYE " GPU", &m_statMode, 2 );
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::SeparatorEx( ImGuiSeparatorFlags_Vertical );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
|
|
|
|
Vector<SrcLocZonesSlim> srcloc;
|
|
|
|
bool copySrclocsToClipboard = false;
|
|
if( m_statMode == 0 )
|
|
{
|
|
if( !m_worker.AreSourceLocationZonesReady() )
|
|
{
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::PopStyleVar();
|
|
ImGui::TextWrapped( "Please wait, computing data..." );
|
|
DrawWaitingDots( s_time );
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const auto filterActive = m_statisticsFilter.IsActive();
|
|
auto& slz = m_worker.GetSourceLocationZones();
|
|
srcloc.reserve( slz.size() );
|
|
uint32_t slzcnt = 0;
|
|
if( m_statRange.active )
|
|
{
|
|
const auto min = m_statRange.min;
|
|
const auto max = m_statRange.max;
|
|
const auto st = max - min;
|
|
for( auto it = slz.begin(); it != slz.end(); ++it )
|
|
{
|
|
if( it->second.total != 0 && it->second.min <= st )
|
|
{
|
|
if( !filterActive )
|
|
{
|
|
auto cit = m_statCache.find( it->first );
|
|
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 )
|
|
{
|
|
slzcnt++;
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.threadNum, cit->second.count, cit->second.total } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unordered_flat_set<uint16_t> threads;
|
|
size_t cnt = 0;
|
|
int64_t total = 0;
|
|
for( auto& v : it->second.zones )
|
|
{
|
|
auto& z = *v.Zone();
|
|
const auto start = z.Start();
|
|
const auto end = z.End();
|
|
if( start >= min && end <= max )
|
|
{
|
|
const auto zt = end - start;
|
|
if( m_statAccumulationMode == AccumulationMode::SelfOnly )
|
|
{
|
|
total += zt - GetZoneChildTimeFast( z );
|
|
cnt++;
|
|
threads.emplace( v.Thread() );
|
|
}
|
|
else if( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry( z ) )
|
|
{
|
|
total += zt;
|
|
cnt++;
|
|
threads.emplace( v.Thread() );
|
|
}
|
|
}
|
|
}
|
|
const auto threadNum = (uint16_t)threads.size();
|
|
if( cnt != 0 )
|
|
{
|
|
slzcnt++;
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, threadNum, cnt, total } );
|
|
}
|
|
m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total, threadNum };
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slzcnt++;
|
|
auto& sl = m_worker.GetSourceLocation( it->first );
|
|
auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
|
|
if( m_statisticsFilter.PassFilter( name ) )
|
|
{
|
|
auto cit = m_statCache.find( it->first );
|
|
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 )
|
|
{
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.threadNum, cit->second.count, cit->second.total } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
unordered_flat_set<uint16_t> threads;
|
|
size_t cnt = 0;
|
|
int64_t total = 0;
|
|
for( auto& v : it->second.zones )
|
|
{
|
|
auto& z = *v.Zone();
|
|
const auto start = z.Start();
|
|
const auto end = z.End();
|
|
if( start >= min && end <= max )
|
|
{
|
|
const auto zt = end - start;
|
|
if( m_statAccumulationMode == AccumulationMode::SelfOnly )
|
|
{
|
|
total += zt - GetZoneChildTimeFast( z );
|
|
cnt++;
|
|
threads.emplace( v.Thread() );
|
|
}
|
|
else if( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry( z ) )
|
|
{
|
|
total += zt;
|
|
cnt++;
|
|
threads.emplace( v.Thread() );
|
|
}
|
|
}
|
|
}
|
|
const auto threadNum = (uint16_t)threads.size();
|
|
if( cnt != 0 )
|
|
{
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, threadNum, cnt, total } );
|
|
}
|
|
m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total, threadNum };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( auto it = slz.begin(); it != slz.end(); ++it )
|
|
{
|
|
if( it->second.total != 0 )
|
|
{
|
|
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 )
|
|
{
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, (uint16_t)it->second.threadCnt.size(), count, total } );
|
|
}
|
|
else
|
|
{
|
|
auto& sl = m_worker.GetSourceLocation( it->first );
|
|
auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
|
|
if( m_statisticsFilter.PassFilter( name ) )
|
|
{
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, (uint16_t)it->second.threadCnt.size(), count, total } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TextFocused( "Total zone count:", RealToString( slzcnt ) );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
TextFocused( "Visible zones:", RealToString( srcloc.size() ) );
|
|
ImGui::SameLine();
|
|
copySrclocsToClipboard = ClipboardButton();
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
AccumulationModeComboBox();
|
|
}
|
|
else if( m_statMode == 1 )
|
|
{
|
|
ImGui::Checkbox( ICON_FA_STOPWATCH " Show time", &m_statSampleTime );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
if( m_statRange.active )
|
|
{
|
|
ImGui::BeginDisabled();
|
|
m_statAccumulationMode = AccumulationMode::SelfOnly;
|
|
AccumulationModeComboBox();
|
|
ImGui::EndDisabled();
|
|
}
|
|
else
|
|
{
|
|
AccumulationModeComboBox();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox( ICON_FA_EYE_SLASH " Hide unknown", &m_statHideUnknown );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox( ICON_FA_PUZZLE_PIECE " Show all", &m_showAllSymbols );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox( ICON_FA_SITEMAP " Inlines", &m_statSeparateInlines );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox( ICON_FA_AT " Address", &m_statShowAddress );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::TextUnformatted( "Location:" );
|
|
ImGui::SameLine();
|
|
const char* locationTable = "Entry\0Sample\0Smart\0";
|
|
ImGui::SetNextItemWidth( ImGui::CalcTextSize( "Sample" ).x + ImGui::GetTextLineHeight() * 2 );
|
|
ImGui::Combo( "##location", &m_statSampleLocation, locationTable );
|
|
}
|
|
else
|
|
{
|
|
assert( m_statMode == 2 );
|
|
if( !m_worker.AreGpuSourceLocationZonesReady() )
|
|
{
|
|
ImGui::Spacing();
|
|
ImGui::Separator();
|
|
ImGui::PopStyleVar();
|
|
ImGui::TextWrapped( "Please wait, computing data..." );
|
|
DrawWaitingDots( s_time );
|
|
ImGui::End();
|
|
return;
|
|
}
|
|
|
|
const auto filterActive = m_statisticsFilter.IsActive();
|
|
auto& slz = m_worker.GetGpuSourceLocationZones();
|
|
srcloc.reserve( slz.size() );
|
|
uint32_t slzcnt = 0;
|
|
if( m_statRange.active )
|
|
{
|
|
const auto min = m_statRange.min;
|
|
const auto max = m_statRange.max;
|
|
const auto st = max - min;
|
|
for( auto it = slz.begin(); it != slz.end(); ++it )
|
|
{
|
|
if( it->second.total != 0 && it->second.min <= st )
|
|
{
|
|
if( !filterActive )
|
|
{
|
|
auto cit = m_gpuStatCache.find( it->first );
|
|
if( cit != m_gpuStatCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
|
|
{
|
|
if( cit->second.count != 0 )
|
|
{
|
|
slzcnt++;
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, 0, cit->second.count, cit->second.total } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cnt = 0;
|
|
int64_t total = 0;
|
|
for( auto& v : it->second.zones )
|
|
{
|
|
auto& z = *v.Zone();
|
|
const auto start = z.GpuStart();
|
|
const auto end = z.GpuEnd();
|
|
if( start >= min && end <= max )
|
|
{
|
|
const auto zt = end - start;
|
|
total += zt;
|
|
cnt++;
|
|
}
|
|
}
|
|
if( cnt != 0 )
|
|
{
|
|
slzcnt++;
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, 0, cnt, total } );
|
|
}
|
|
m_gpuStatCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
|
|
}
|
|
}
|
|
else
|
|
{
|
|
slzcnt++;
|
|
auto& sl = m_worker.GetSourceLocation( it->first );
|
|
auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
|
|
if( m_statisticsFilter.PassFilter( name ) )
|
|
{
|
|
auto cit = m_gpuStatCache.find( it->first );
|
|
if( cit != m_gpuStatCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
|
|
{
|
|
if( cit->second.count != 0 )
|
|
{
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, 0, cit->second.count, cit->second.total } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t cnt = 0;
|
|
int64_t total = 0;
|
|
for( auto& v : it->second.zones )
|
|
{
|
|
auto& z = *v.Zone();
|
|
const auto start = z.GpuStart();
|
|
const auto end = z.GpuEnd();
|
|
if( start >= min && end <= max )
|
|
{
|
|
const auto zt = end - start;
|
|
total += zt;
|
|
cnt++;
|
|
}
|
|
}
|
|
if( cnt != 0 )
|
|
{
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, 0, cnt, total } );
|
|
}
|
|
m_gpuStatCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( auto it = slz.begin(); it != slz.end(); ++it )
|
|
{
|
|
if( it->second.total != 0 )
|
|
{
|
|
slzcnt++;
|
|
size_t count = it->second.zones.size();
|
|
int64_t total = it->second.total;
|
|
if( !filterActive )
|
|
{
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, 0, count, total } );
|
|
}
|
|
else
|
|
{
|
|
auto& sl = m_worker.GetSourceLocation( it->first );
|
|
auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
|
|
if( m_statisticsFilter.PassFilter( name ) )
|
|
{
|
|
srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, 0, count, total } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
TextFocused( "Total zone count:", RealToString( slzcnt ) );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
TextFocused( "Visible zones:", RealToString( srcloc.size() ) );
|
|
ImGui::SameLine();
|
|
copySrclocsToClipboard = ClipboardButton();
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::AlignTextToFramePadding();
|
|
TextDisabledUnformatted( "Filter results" );
|
|
ImGui::SameLine();
|
|
m_statisticsFilter.Draw( ICON_FA_FILTER "###resultFilter", 200 );
|
|
ImGui::SameLine();
|
|
if( ImGui::Button( ICON_FA_DELETE_LEFT " Clear" ) )
|
|
{
|
|
m_statisticsFilter.Clear();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
if( m_statMode == 1 )
|
|
{
|
|
TextDisabledUnformatted( "Image name" );
|
|
ImGui::SameLine();
|
|
m_statisticsImageFilter.Draw( ICON_FA_FILTER "###imageFilter", 200 );
|
|
ImGui::SameLine();
|
|
if( ImGui::BeginCombo( "###imageCombo", nullptr, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLarge ) )
|
|
{
|
|
unordered_flat_set<StringIdx, StringIdxHasher, StringIdxComparator> set;
|
|
std::vector<const char*> imgNames;
|
|
for( auto& v : m_worker.GetSymbolMap() )
|
|
{
|
|
auto it = set.find( v.second.imageName );
|
|
if( it == set.end() )
|
|
{
|
|
set.emplace( v.second.imageName );
|
|
}
|
|
}
|
|
imgNames.reserve( set.size() );
|
|
for( auto& img : set )
|
|
{
|
|
imgNames.emplace_back( m_worker.GetString( img ) );
|
|
}
|
|
std::sort( imgNames.begin(), imgNames.end(), [] ( const auto& lhs, const auto& rhs ) { return strcmp( lhs, rhs ) < 0; } );
|
|
for( auto& img : imgNames )
|
|
{
|
|
bool sel = false;
|
|
if( ImGui::Selectable( img, &sel ) )
|
|
{
|
|
auto len = std::min<size_t>( 255, strlen( img ) );
|
|
memcpy( m_statisticsImageFilter.InputBuf, img, len );
|
|
m_statisticsImageFilter.InputBuf[len] = 0;
|
|
m_statisticsImageFilter.Build();
|
|
}
|
|
}
|
|
ImGui::EndCombo();
|
|
}
|
|
ImGui::SameLine();
|
|
if( ImGui::Button( ICON_FA_DELETE_LEFT " Clear###image" ) )
|
|
{
|
|
m_statisticsImageFilter.Clear();
|
|
}
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
ImGui::Checkbox( ICON_FA_HAT_WIZARD " Include kernel", &m_statShowKernel );
|
|
ImGui::SameLine();
|
|
ImGui::Spacing();
|
|
ImGui::SameLine();
|
|
}
|
|
if( m_statMode == 1 && !m_worker.AreSymbolSamplesReady() )
|
|
{
|
|
m_statRange.active = false;
|
|
bool val = false;
|
|
ImGui::PushItemFlag( ImGuiItemFlags_Disabled, true );
|
|
ImGui::PushStyleVar( ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f );
|
|
ImGui::Checkbox( "Limit range", &val );
|
|
ImGui::PopItemFlag();
|
|
ImGui::PopStyleVar();
|
|
TooltipIfHovered( "Waiting for background tasks to finish" );
|
|
}
|
|
else
|
|
{
|
|
if( ImGui::Checkbox( "Limit range", &m_statRange.active ) )
|
|
{
|
|
if( m_statRange.active && m_statRange.min == 0 && m_statRange.max == 0 )
|
|
{
|
|
m_statRange.min = m_vd.zvStart;
|
|
m_statRange.max = m_vd.zvEnd;
|
|
}
|
|
}
|
|
if( m_statRange.active )
|
|
{
|
|
ImGui::SameLine();
|
|
TextColoredUnformatted( 0xFF00FFFF, ICON_FA_TRIANGLE_EXCLAMATION );
|
|
ImGui::SameLine();
|
|
ToggleButton( ICON_FA_RULER " Limits", m_showRanges );
|
|
}
|
|
}
|
|
|
|
ImGui::Separator();
|
|
ImGui::PopStyleVar();
|
|
|
|
int64_t timeRange;
|
|
if( m_statRange.active )
|
|
{
|
|
const auto st = m_statRange.max - m_statRange.min;
|
|
timeRange = st == 0 ? 1 : st;
|
|
}
|
|
else
|
|
{
|
|
timeRange = m_worker.GetLastTime() - m_worker.GetFirstTime();
|
|
}
|
|
|
|
if( m_statMode == 0 || m_statMode == 2 )
|
|
{
|
|
if( srcloc.empty() )
|
|
{
|
|
ImGui::TextUnformatted( "No entries to be displayed." );
|
|
}
|
|
else
|
|
{
|
|
ImGui::BeginChild( "##statistics" );
|
|
if( ImGui::BeginTable( "##statistics", m_statMode == 0 ? 6 : 5,
|
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY ) )
|
|
{
|
|
ImGui::TableSetupScrollFreeze( 0, 1 );
|
|
ImGui::TableSetupColumn( "Name", ImGuiTableColumnFlags_NoHide );
|
|
ImGui::TableSetupColumn( "Location" );
|
|
ImGui::TableSetupColumn( "Total time", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
|
|
ImGui::TableSetupColumn( "Counts", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
|
|
ImGui::TableSetupColumn( "MTPC", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
|
|
if( m_statMode == 0 ) ImGui::TableSetupColumn( ICON_FA_SHUFFLE, ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
|
|
ImGui::TableHeadersRow();
|
|
|
|
const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs;
|
|
switch( sortspec.ColumnIndex )
|
|
{
|
|
case 0:
|
|
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), [this]( const auto& lhs, const auto& rhs ) { return strcmp( m_worker.GetZoneName( m_worker.GetSourceLocation( lhs.srcloc ) ), m_worker.GetZoneName( m_worker.GetSourceLocation( rhs.srcloc ) ) ) < 0; } );
|
|
}
|
|
else
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), [this]( const auto& lhs, const auto& rhs ) { return strcmp( m_worker.GetZoneName( m_worker.GetSourceLocation( lhs.srcloc ) ), m_worker.GetZoneName( m_worker.GetSourceLocation( rhs.srcloc ) ) ) > 0; } );
|
|
}
|
|
break;
|
|
case 1:
|
|
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), [this]( const auto& lhs, const auto& rhs ) {
|
|
const auto& sll = m_worker.GetSourceLocation( lhs.srcloc );
|
|
const auto& slr = m_worker.GetSourceLocation( rhs.srcloc );
|
|
const auto cmp = strcmp( m_worker.GetString( sll.file ), m_worker.GetString( slr.file ) );
|
|
if( cmp == 0 ) return sll.line < slr.line;
|
|
return cmp < 0;
|
|
} );
|
|
}
|
|
else
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), [this]( const auto& lhs, const auto& rhs ) {
|
|
const auto& sll = m_worker.GetSourceLocation( lhs.srcloc );
|
|
const auto& slr = m_worker.GetSourceLocation( rhs.srcloc );
|
|
const auto cmp = strcmp( m_worker.GetString( sll.file ), m_worker.GetString( slr.file ) );
|
|
if( cmp == 0 ) return sll.line > slr.line;
|
|
return cmp > 0;
|
|
} );
|
|
}
|
|
break;
|
|
case 2:
|
|
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
|
|
{
|
|
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;
|
|
case 3:
|
|
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.numZones < rhs.numZones; } );
|
|
}
|
|
else
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.numZones > rhs.numZones; } );
|
|
}
|
|
break;
|
|
case 4:
|
|
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; } );
|
|
}
|
|
else
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones > rhs.total / rhs.numZones; } );
|
|
}
|
|
break;
|
|
case 5:
|
|
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.numThreads < rhs.numThreads; } );
|
|
}
|
|
else
|
|
{
|
|
pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.numThreads > rhs.numThreads; } );
|
|
}
|
|
break;
|
|
default:
|
|
assert( false );
|
|
break;
|
|
}
|
|
|
|
std::ostringstream clipboardCSV;
|
|
if( copySrclocsToClipboard )
|
|
{
|
|
clipboardCSV << "name" << ',' << "src_file" << ',' << "src_line" << ',' << "total_ns" << ',' << "counts" << "\n";
|
|
}
|
|
|
|
for( auto& v : srcloc )
|
|
{
|
|
ImGui::TableNextRow();
|
|
ImGui::TableNextColumn();
|
|
|
|
ImGui::PushID( v.srcloc );
|
|
auto& srcloc = m_worker.GetSourceLocation( v.srcloc );
|
|
auto name = m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function );
|
|
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
|
|
ImGui::SameLine();
|
|
if( m_statMode == 0 )
|
|
{
|
|
if( ImGui::Selectable( name, m_findZone.show && !m_findZone.match.empty() && m_findZone.match[m_findZone.selMatch] == v.srcloc, ImGuiSelectableFlags_SpanAllColumns ) )
|
|
{
|
|
m_findZone.ShowZone( v.srcloc, name );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ImGui::TextUnformatted( name );
|
|
}
|
|
ImGui::TableNextColumn();
|
|
float indentVal = 0.f;
|
|
if( m_statBuzzAnim.Match( v.srcloc ) )
|
|
{
|
|
const auto time = m_statBuzzAnim.Time();
|
|
indentVal = sin( time * 60.f ) * 10.f * time;
|
|
ImGui::Indent( indentVal );
|
|
}
|
|
const auto file = m_worker.GetString( srcloc.file );
|
|
|
|
TextDisabledUnformatted( LocationToString( file, srcloc.line ) );
|
|
if( ImGui::IsItemHovered() )
|
|
{
|
|
DrawSourceTooltip( file, srcloc.line );
|
|
if( ImGui::IsItemClicked( 1 ) )
|
|
{
|
|
if( SourceFileValid( file, m_worker.GetCaptureTime(), *this, m_worker ) )
|
|
{
|
|
ViewSourceCheckKeyMod( file, srcloc.line, m_worker.GetString( srcloc.function ) );
|
|
}
|
|
else
|
|
{
|
|
m_statBuzzAnim.Enable( v.srcloc, 0.5f );
|
|
}
|
|
}
|
|
}
|
|
if( indentVal != 0.f )
|
|
{
|
|
ImGui::Unindent( indentVal );
|
|
}
|
|
ImGui::TableNextColumn();
|
|
const auto time = v.total;
|
|
ImGui::TextUnformatted( TimeToString( time ) );
|
|
ImGui::SameLine();
|
|
char buf[64];
|
|
PrintStringPercent( buf, 100. * time / timeRange );
|
|
TextDisabledUnformatted( buf );
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted( RealToString( v.numZones ) );
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted( TimeToString( time / v.numZones ) );
|
|
if( m_statMode == 0 )
|
|
{
|
|
ImGui::TableNextColumn();
|
|
ImGui::TextUnformatted( RealToString( v.numThreads ) );
|
|
}
|
|
ImGui::PopID();
|
|
|
|
if( copySrclocsToClipboard )
|
|
{
|
|
clipboardCSV << name << ',' << file << ',' << srcloc.line << ',' << v.total << ',' << v.numZones << '\n';
|
|
}
|
|
}
|
|
ImGui::EndTable();
|
|
|
|
if( copySrclocsToClipboard )
|
|
{
|
|
std::string clipboardCSVString = clipboardCSV.str();
|
|
ImGui::SetClipboardText( clipboardCSVString.c_str() );
|
|
}
|
|
}
|
|
ImGui::EndChild();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
assert( m_statMode == 1 );
|
|
const auto& symMap = m_worker.GetSymbolMap();
|
|
const auto& symStat = m_worker.GetSymbolStats();
|
|
|
|
Vector<SymList> data;
|
|
if( m_showAllSymbols )
|
|
{
|
|
data.reserve( symMap.size() );
|
|
if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() || !m_statShowKernel )
|
|
{
|
|
for( auto& v : symMap )
|
|
{
|
|
const auto name = m_worker.GetString( v.second.name );
|
|
const auto image = m_worker.GetString( v.second.imageName );
|
|
bool pass = ( m_statShowKernel || ( v.first >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
|
|
if( !pass && v.second.size.Val() == 0 )
|
|
{
|
|
const auto parentAddr = m_worker.GetSymbolForAddress( v.first );
|
|
if( parentAddr != 0 )
|
|
{
|
|
auto pit = symMap.find( parentAddr );
|
|
if( pit != symMap.end() )
|
|
{
|
|
const auto parentName = m_worker.GetString( pit->second.name );
|
|
pass = ( m_statShowKernel || ( parentAddr >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
|
|
}
|
|
}
|
|
}
|
|
if( pass )
|
|
{
|
|
auto it = symStat.find( v.first );
|
|
if( it == symStat.end() )
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, 0, 0 } );
|
|
}
|
|
else
|
|
{
|
|
if( m_statRange.active )
|
|
{
|
|
auto samples = m_worker.GetSamplesForSymbol( v.first );
|
|
if( samples )
|
|
{
|
|
auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
|
|
if( it != samples->end() )
|
|
{
|
|
auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
|
|
const auto count = uint32_t( end - it );
|
|
data.push_back_no_space_check( SymList { v.first, 0, count } );
|
|
}
|
|
else
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, 0, 0 } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, 0, 0 } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, it->second.incl, it->second.excl } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( auto& v : symMap )
|
|
{
|
|
auto it = symStat.find( v.first );
|
|
if( it == symStat.end() )
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, 0, 0 } );
|
|
}
|
|
else
|
|
{
|
|
if( m_statRange.active )
|
|
{
|
|
auto samples = m_worker.GetSamplesForSymbol( v.first );
|
|
if( samples )
|
|
{
|
|
auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
|
|
if( it != samples->end() )
|
|
{
|
|
auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
|
|
const auto count = uint32_t( end - it );
|
|
data.push_back_no_space_check( SymList { v.first, 0, count } );
|
|
}
|
|
else
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, 0, 0 } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, 0, 0 } );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, it->second.incl, it->second.excl } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data.reserve( symStat.size() );
|
|
if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() || !m_statShowKernel )
|
|
{
|
|
for( auto& v : symStat )
|
|
{
|
|
auto sit = symMap.find( v.first );
|
|
if( sit != symMap.end() )
|
|
{
|
|
const auto name = m_worker.GetString( sit->second.name );
|
|
const auto image = m_worker.GetString( sit->second.imageName );
|
|
bool pass = ( m_statShowKernel || ( v.first >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
|
|
if( !pass && sit->second.size.Val() == 0 )
|
|
{
|
|
const auto parentAddr = m_worker.GetSymbolForAddress( v.first );
|
|
if( parentAddr != 0 )
|
|
{
|
|
auto pit = symMap.find( parentAddr );
|
|
if( pit != symMap.end() )
|
|
{
|
|
const auto parentName = m_worker.GetString( pit->second.name );
|
|
pass = ( m_statShowKernel || ( parentAddr >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
|
|
}
|
|
}
|
|
}
|
|
if( pass )
|
|
{
|
|
if( m_statRange.active )
|
|
{
|
|
auto samples = m_worker.GetSamplesForSymbol( v.first );
|
|
if( samples )
|
|
{
|
|
auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
|
|
if( it != samples->end() )
|
|
{
|
|
auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
|
|
const auto count = uint32_t( end - it );
|
|
data.push_back_no_space_check( SymList { v.first, 0, count } );
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, v.second.incl, v.second.excl } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( m_statRange.active )
|
|
{
|
|
for( auto& v : symStat )
|
|
{
|
|
auto samples = m_worker.GetSamplesForSymbol( v.first );
|
|
if( samples )
|
|
{
|
|
auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
|
|
if( it != samples->end() )
|
|
{
|
|
auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
|
|
const auto count = uint32_t( end - it );
|
|
data.push_back_no_space_check( SymList { v.first, 0, count } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( auto& v : symStat )
|
|
{
|
|
data.push_back_no_space_check( SymList { v.first, v.second.incl, v.second.excl } );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DrawSamplesStatistics( data, timeRange, m_statAccumulationMode );
|
|
}
|
|
#endif
|
|
ImGui::End();
|
|
}
|
|
|
|
}
|