diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj
index 4560db14..d6f8165b 100644
--- a/profiler/build/win32/Tracy.vcxproj
+++ b/profiler/build/win32/Tracy.vcxproj
@@ -151,6 +151,7 @@
+
diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters
index fca9b148..880774c8 100644
--- a/profiler/build/win32/Tracy.vcxproj.filters
+++ b/profiler/build/win32/Tracy.vcxproj.filters
@@ -300,6 +300,9 @@
server
+
+ server
+
diff --git a/server/TracyView.cpp b/server/TracyView.cpp
index e8263818..6d9afbf2 100644
--- a/server/TracyView.cpp
+++ b/server/TracyView.cpp
@@ -5393,490 +5393,6 @@ void View::DrawHistogramMinMaxLabel( ImDrawList* draw, int64_t tmin, int64_t tma
draw->AddText( wpos + ImVec2( round( (w-1-rsz) * 0.5 ), ty15 ), 0x66FFFFFF, range );
}
-void View::DrawSamplesStatistics( Vector& data, int64_t timeRange, AccumulationMode accumulationMode )
-{
- static unordered_flat_map inlineMap;
- assert( inlineMap.empty() );
- if( !m_statSeparateInlines )
- {
- static unordered_flat_map baseMap;
- assert( baseMap.empty() );
- for( auto& v : data )
- {
- auto sym = m_worker.GetSymbolData( v.symAddr );
- const auto symAddr = ( sym && sym->isInline ) ? m_worker.GetSymbolForAddress( v.symAddr ) : v.symAddr;
- auto it = baseMap.find( symAddr );
- if( it == baseMap.end() )
- {
- baseMap.emplace( symAddr, SymList { symAddr, v.incl, v.excl, 0 } );
- }
- else
- {
- assert( symAddr == it->second.symAddr );
- it->second.incl += v.incl;
- it->second.excl += v.excl;
- it->second.count++;
- }
- }
- for( auto& v : data ) inlineMap.emplace( v.symAddr, SymList { v.symAddr, v.incl, v.excl, v.count } );
- data.clear();
- for( auto& v : baseMap )
- {
- data.push_back_no_space_check( v.second );
- }
- baseMap.clear();
- }
-
- if( data.empty() )
- {
- ImGui::TextUnformatted( "No entries to be displayed." );
- }
- else
- {
- const auto& symMap = m_worker.GetSymbolMap();
-
- if( accumulationMode == 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; } );
- }
- else
- {
- pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l.incl != r.incl ? l.incl > r.incl : l.symAddr < r.symAddr; } );
- }
-
- ImGui::BeginChild( "##statisticsSampling" );
- if( ImGui::BeginTable( "##statisticsSampling", 5, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY ) )
- {
- ImGui::TableSetupScrollFreeze( 0, 1 );
- ImGui::TableSetupColumn( "Name", ImGuiTableColumnFlags_NoHide );
- ImGui::TableSetupColumn( "Location", ImGuiTableColumnFlags_NoSort );
- ImGui::TableSetupColumn( "Image" );
- ImGui::TableSetupColumn( m_statSampleTime ? "Time" : "Count", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
- ImGui::TableSetupColumn( "Code size", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
- ImGui::TableHeadersRow();
-
- double revSampleCount100;
- if( m_statRange.active && m_worker.GetSamplingPeriod() != 0 )
- {
- const auto st = m_statRange.max - m_statRange.min;
- const auto cnt = st / m_worker.GetSamplingPeriod();
- revSampleCount100 = 100. / cnt;
- }
- else
- {
- revSampleCount100 = 100. / m_worker.GetCallstackSampleCount();
- }
-
- const bool showAll = m_showAllSymbols;
- const auto period = m_worker.GetSamplingPeriod();
- int idx = 0;
- for( auto& v : data )
- {
- const auto cnt = accumulationMode == AccumulationMode::SelfOnly ? v.excl : v.incl;
- if( cnt > 0 || showAll )
- {
- const char* name = "[unknown]";
- const char* file = "[unknown]";
- const char* imageName = "[unknown]";
- uint32_t line = 0;
- bool isInline = false;
- uint32_t symlen = 0;
- auto codeAddr = v.symAddr;
-
- auto sit = symMap.find( v.symAddr );
- if( sit != symMap.end() )
- {
- name = m_worker.GetString( sit->second.name );
- imageName = m_worker.GetString( sit->second.imageName );
- isInline = sit->second.isInline;
- switch( m_statSampleLocation )
- {
- case 0:
- file = m_worker.GetString( sit->second.file );
- line = sit->second.line;
- break;
- case 1:
- file = m_worker.GetString( sit->second.callFile );
- line = sit->second.callLine;
- break;
- case 2:
- if( sit->second.isInline )
- {
- file = m_worker.GetString( sit->second.callFile );
- line = sit->second.callLine;
- }
- else
- {
- file = m_worker.GetString( sit->second.file );
- line = sit->second.line;
- }
- break;
- default:
- assert( false );
- break;
- }
- if( m_statHideUnknown && file[0] == '[' ) continue;
- symlen = sit->second.size.Val();
- }
- else if( m_statHideUnknown )
- {
- continue;
- }
-
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
-
- const bool isKernel = v.symAddr >> 63 != 0;
- const char* parentName = nullptr;
- if( symlen == 0 && !isKernel )
- {
- const auto parentAddr = m_worker.GetSymbolForAddress( v.symAddr );
- if( parentAddr != 0 )
- {
- auto pit = symMap.find( parentAddr );
- if( pit != symMap.end() )
- {
- codeAddr = parentAddr;
- symlen = pit->second.size.Val();
- parentName = m_worker.GetString( pit->second.name );
- }
- }
- }
-
- bool expand = false;
- if( !m_statSeparateInlines )
- {
- if( v.count > 0 && v.symAddr != 0 )
- {
- ImGui::PushID( v.symAddr );
- expand = ImGui::TreeNodeEx( "", v.count == 0 ? ImGuiTreeNodeFlags_Leaf : 0 );
- ImGui::PopID();
- ImGui::SameLine();
- }
- }
- else if( isInline )
- {
- TextDisabledUnformatted( ICON_FA_CARET_RIGHT );
- ImGui::SameLine();
- }
- uint32_t excl;
- if( m_statSeparateInlines )
- {
- excl = v.excl;
- }
- else
- {
- auto it = inlineMap.find( v.symAddr );
- excl = it != inlineMap.end() ? it->second.excl : 0;
- }
- bool hasNoSamples = v.symAddr == 0 || excl == 0;
- if( !m_statSeparateInlines && hasNoSamples && v.symAddr != 0 && v.count > 0 )
- {
- auto inSym = m_worker.GetInlineSymbolList( v.symAddr, symlen );
- if( inSym )
- {
- const auto symEnd = v.symAddr + symlen;
- while( *inSym < symEnd )
- {
- auto sit = inlineMap.find( *inSym );
- if( sit != inlineMap.end() )
- {
- if( sit->second.excl != 0 )
- {
- hasNoSamples = false;
- break;
- }
- }
- inSym++;
- }
- }
- }
- if( hasNoSamples )
- {
- if( isKernel )
- {
- TextColoredUnformatted( 0xFF8888FF, name );
- }
- else
- {
- ImGui::TextUnformatted( name );
- }
- }
- else
- {
- ImGui::PushID( idx++ );
- if( isKernel ) ImGui::PushStyleColor( ImGuiCol_Text, 0xFF8888FF );
- const auto clicked = ImGui::Selectable( name, m_sampleParents.withInlines && m_sampleParents.symAddr == v.symAddr, ImGuiSelectableFlags_SpanAllColumns );
- if( isKernel ) ImGui::PopStyleColor();
- if( clicked ) ShowSampleParents( v.symAddr, !m_statSeparateInlines );
- ImGui::PopID();
- }
- if( parentName )
- {
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", parentName );
- }
- if( !m_statSeparateInlines && v.count > 0 && v.symAddr != 0 )
- {
- ImGui::SameLine();
- ImGui::TextDisabled( "(+%s)", RealToString( v.count ) );
- }
- ImGui::TableNextColumn();
- float indentVal = 0.f;
- if( m_statBuzzAnim.Match( v.symAddr ) )
- {
- const auto time = m_statBuzzAnim.Time();
- indentVal = sin( time * 60.f ) * 10.f * time;
- ImGui::Indent( indentVal );
- }
- if( m_statShowAddress )
- {
- ImGui::TextDisabled( "0x%" PRIx64, v.symAddr );
- }
- else
- {
- TextDisabledUnformatted( LocationToString( file, line ) );
- }
- if( ImGui::IsItemHovered() )
- {
- DrawSourceTooltip( file, line );
- if( ImGui::IsItemClicked( 1 ) )
- {
- if( SourceFileValid( file, m_worker.GetCaptureTime(), *this, m_worker ) )
- {
- ViewSymbol( file, line, codeAddr, v.symAddr );
- if( !m_statSeparateInlines ) m_sourceView->CalcInlineStats( false );
- }
- else if( symlen != 0 )
- {
- uint32_t len;
- if( m_worker.GetSymbolCode( codeAddr, len ) )
- {
- ViewSymbol( nullptr, 0, codeAddr, v.symAddr );
- if( !m_statSeparateInlines ) m_sourceView->CalcInlineStats( false );
- }
- else
- {
- m_statBuzzAnim.Enable( v.symAddr, 0.5f );
- }
- }
- else
- {
- m_statBuzzAnim.Enable( v.symAddr, 0.5f );
- }
- }
- }
- if( indentVal != 0.f )
- {
- ImGui::Unindent( indentVal );
- }
- ImGui::TableNextColumn();
- TextDisabledUnformatted( imageName );
- ImGui::TableNextColumn();
- if( cnt > 0 )
- {
- char buf[64];
- if( m_statSampleTime )
- {
- const auto t = cnt * period;
- ImGui::TextUnformatted( TimeToString( t ) );
- PrintStringPercent( buf, 100. * t / timeRange );
- }
- else
- {
- ImGui::TextUnformatted( RealToString( cnt ) );
- PrintStringPercent( buf, cnt * revSampleCount100 );
- }
- ImGui::SameLine();
- TextDisabledUnformatted( buf );
- }
- ImGui::TableNextColumn();
- if( symlen != 0 )
- {
- if( m_worker.HasSymbolCode( codeAddr ) )
- {
- TextDisabledUnformatted( ICON_FA_DATABASE );
- ImGui::SameLine();
- }
- if( isInline )
- {
- TextDisabledUnformatted( "<" );
- ImGui::SameLine();
- }
- TextDisabledUnformatted( MemSizeToString( symlen ) );
- }
-
- if( !m_statSeparateInlines && expand )
- {
- assert( v.count > 0 );
- assert( symlen != 0 );
- auto inSym = m_worker.GetInlineSymbolList( v.symAddr, symlen );
- assert( inSym != nullptr );
- const auto symEnd = v.symAddr + symlen;
- Vector inSymList;
- while( *inSym < symEnd )
- {
- auto sit = inlineMap.find( *inSym );
- if( sit != inlineMap.end() )
- {
- inSymList.push_back( SymList { *inSym, sit->second.incl, sit->second.excl } );
- }
- else
- {
- inSymList.push_back( SymList { *inSym, 0, 0 } );
- }
- inSym++;
- }
- auto statIt = inlineMap.find( v.symAddr );
- if( statIt != inlineMap.end() )
- {
- inSymList.push_back( SymList { v.symAddr, statIt->second.incl, statIt->second.excl } );
- }
-
- if( accumulationMode == 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; } );
- }
- else
- {
- pdqsort_branchless( inSymList.begin(), inSymList.end(), []( const auto& l, const auto& r ) { return l.incl != l.incl ? l.incl > r.incl : l.symAddr < r.symAddr; } );
- }
-
- ImGui::Indent();
- for( auto& iv : inSymList )
- {
- const auto cnt = accumulationMode == AccumulationMode::SelfOnly ? iv.excl : iv.incl;
- if( cnt > 0 || showAll )
- {
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- auto sit = symMap.find( iv.symAddr );
- assert( sit != symMap.end() );
- name = m_worker.GetString( sit->second.name );
- switch( m_statSampleLocation )
- {
- case 0:
- file = m_worker.GetString( sit->second.file );
- line = sit->second.line;
- break;
- case 1:
- file = m_worker.GetString( sit->second.callFile );
- line = sit->second.callLine;
- break;
- case 2:
- if( sit->second.isInline )
- {
- file = m_worker.GetString( sit->second.callFile );
- line = sit->second.callLine;
- }
- else
- {
- file = m_worker.GetString( sit->second.file );
- line = sit->second.line;
- }
- break;
- default:
- assert( false );
- break;
- }
-
- const auto sn = iv.symAddr == v.symAddr ? "[ - self - ]" : name;
- if( iv.excl == 0 )
- {
- ImGui::TextUnformatted( sn );
- }
- else
- {
- ImGui::PushID( idx++ );
- if( ImGui::Selectable( sn, !m_sampleParents.withInlines && m_sampleParents.symAddr == iv.symAddr, ImGuiSelectableFlags_SpanAllColumns ) )
- {
- ShowSampleParents( iv.symAddr, false );
- }
- ImGui::PopID();
- }
- ImGui::TableNextColumn();
- float indentVal = 0.f;
- if( m_statBuzzAnim.Match( iv.symAddr ) )
- {
- const auto time = m_statBuzzAnim.Time();
- indentVal = sin( time * 60.f ) * 10.f * time;
- ImGui::Indent( indentVal );
- }
- if( m_statShowAddress )
- {
- ImGui::TextDisabled( "0x%" PRIx64, iv.symAddr );
- }
- else
- {
- TextDisabledUnformatted( LocationToString( file, line ) );
- }
- if( ImGui::IsItemHovered() )
- {
- DrawSourceTooltip( file, line );
- if( ImGui::IsItemClicked( 1 ) )
- {
- if( SourceFileValid( file, m_worker.GetCaptureTime(), *this, m_worker ) )
- {
- ViewSymbol( file, line, codeAddr, iv.symAddr );
- if( !m_statSeparateInlines ) m_sourceView->CalcInlineStats( true );
- }
- else if( symlen != 0 )
- {
- uint32_t len;
- if( m_worker.GetSymbolCode( codeAddr, len ) )
- {
- ViewSymbol( nullptr, 0, codeAddr, iv.symAddr );
- if( !m_statSeparateInlines ) m_sourceView->CalcInlineStats( true );
- }
- else
- {
- m_statBuzzAnim.Enable( iv.symAddr, 0.5f );
- }
- }
- else
- {
- m_statBuzzAnim.Enable( iv.symAddr, 0.5f );
- }
- }
- }
- if( indentVal != 0.f )
- {
- ImGui::Unindent( indentVal );
- }
- ImGui::TableNextColumn();
- ImGui::TableNextColumn();
- if( cnt > 0 )
- {
- char buf[64];
- if( m_statSampleTime )
- {
- const auto t = cnt * period;
- ImGui::TextUnformatted( TimeToString( t ) );
- PrintStringPercent( buf, 100. * t / timeRange );
- }
- else
- {
- ImGui::TextUnformatted( RealToString( cnt ) );
- PrintStringPercent( buf, cnt * revSampleCount100 );
- }
- ImGui::SameLine();
- TextDisabledUnformatted( buf );
- }
- }
- }
- ImGui::Unindent();
- ImGui::TreePop();
- }
- }
- }
- ImGui::EndTable();
- }
- ImGui::EndChild();
-
- inlineMap.clear();
- }
-}
-
void View::DrawTextEditor()
{
const auto scale = GetScale();
@@ -6192,380 +5708,6 @@ void View::DrawAnnotationList()
ImGui::End();
}
-void View::DrawSampleParents()
-{
- bool show = true;
- const auto scale = GetScale();
- ImGui::SetNextWindowSize( ImVec2( 1400 * scale, 500 * scale ), ImGuiCond_FirstUseEver );
- ImGui::Begin( "Sample entry call stacks", &show, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
- if( !ImGui::GetCurrentWindowRead()->SkipItems )
- {
- auto ss = m_worker.GetSymbolStats( m_sampleParents.symAddr );
- auto excl = ss->excl;
- auto stats = ss->parents;
-
- const auto symbol = m_worker.GetSymbolData( m_sampleParents.symAddr );
- if( !symbol->isInline && m_sampleParents.withInlines )
- {
- const auto symlen = symbol->size.Val();
- auto inSym = m_worker.GetInlineSymbolList( m_sampleParents.symAddr, symlen );
- if( inSym )
- {
- const auto symEnd = m_sampleParents.symAddr + symlen;
- while( *inSym < symEnd )
- {
- auto istat = m_worker.GetSymbolStats( *inSym++ );
- if( !istat ) continue;
- excl += istat->excl;
- for( auto& v : istat->baseParents )
- {
- auto it = stats.find( v.first );
- if( it == stats.end() )
- {
- stats.emplace( v.first, v.second );
- }
- else
- {
- it->second += v.second;
- }
- }
- }
- }
- }
- assert( !stats.empty() );
-
- ImGui::PushFont( m_bigFont );
- TextFocused( "Symbol:", m_worker.GetString( symbol->name ) );
- if( symbol->isInline )
- {
- ImGui::SameLine();
- TextDisabledUnformatted( "(inline)" );
- }
- else if( !m_sampleParents.withInlines )
- {
- ImGui::SameLine();
- TextDisabledUnformatted( "(without inlines)" );
- }
- ImGui::PopFont();
- TextDisabledUnformatted( "Location:" );
- ImGui::SameLine();
- const auto callFile = m_worker.GetString( symbol->callFile );
- ImGui::TextUnformatted( LocationToString( callFile, symbol->callLine ) );
- if( ImGui::IsItemClicked( 1 ) )
- {
- ViewDispatch( callFile, symbol->callLine, m_sampleParents.symAddr );
- }
- TextDisabledUnformatted( "Entry point:" );
- ImGui::SameLine();
- const auto file = m_worker.GetString( symbol->file );
- ImGui::TextUnformatted( LocationToString( file, symbol->line ) );
- if( ImGui::IsItemClicked( 1 ) )
- {
- ViewDispatch( file, symbol->line, m_sampleParents.symAddr );
- }
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- TextDisabledUnformatted( m_worker.GetString( symbol->imageName ) );
- ImGui::Separator();
- ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 2, 2 ) );
- if( ImGui::RadioButton( ICON_FA_TABLE " List", m_sampleParents.mode == 0 ) ) m_sampleParents.mode = 0;
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- if( ImGui::RadioButton( ICON_FA_TREE " Bottom-up tree", m_sampleParents.mode == 1 ) ) m_sampleParents.mode = 1;
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- if( ImGui::RadioButton( ICON_FA_TREE " Top-down tree", m_sampleParents.mode == 2 ) ) m_sampleParents.mode = 2;
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::SeparatorEx( ImGuiSeparatorFlags_Vertical );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::Checkbox( ICON_FA_STOPWATCH " Show time", &m_statSampleTime );
- ImGui::PopStyleVar();
- ImGui::Separator();
- ImGui::BeginChild( "##sampleParents" );
- switch( m_sampleParents.mode )
- {
- case 0:
- {
- TextDisabledUnformatted( "Entry call stack:" );
- ImGui::SameLine();
- if( ImGui::SmallButton( " " ICON_FA_CARET_LEFT " " ) )
- {
- m_sampleParents.sel = std::max( m_sampleParents.sel - 1, 0 );
- }
- ImGui::SameLine();
- ImGui::Text( "%s / %s", RealToString( m_sampleParents.sel + 1 ), RealToString( stats.size() ) );
- if( ImGui::IsItemClicked() ) ImGui::OpenPopup( "EntryCallStackPopup" );
- ImGui::SameLine();
- if( ImGui::SmallButton( " " ICON_FA_CARET_RIGHT " " ) )
- {
- m_sampleParents.sel = std::min( m_sampleParents.sel + 1, stats.size() - 1 );
- }
- if( ImGui::BeginPopup( "EntryCallStackPopup" ) )
- {
- int sel = m_sampleParents.sel + 1;
- ImGui::SetNextItemWidth( 120 * scale );
- const bool clicked = ImGui::InputInt( "##entryCallStack", &sel, 1, 100, ImGuiInputTextFlags_EnterReturnsTrue );
- if( clicked ) m_sampleParents.sel = std::min( std::max( sel, 1 ), int( stats.size() ) ) - 1;
- ImGui::EndPopup();
- }
- Vector data;
- data.reserve( stats.size() );
- for( auto it = stats.begin(); it != stats.end(); ++it ) data.push_back( it );
- pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l->second > r->second; } );
- ImGui::SameLine();
- ImGui::TextUnformatted( m_statSampleTime ? TimeToString( m_worker.GetSamplingPeriod() * data[m_sampleParents.sel]->second ) : RealToString( data[m_sampleParents.sel]->second ) );
- ImGui::SameLine();
- char buf[64];
- PrintStringPercent( buf, 100. * data[m_sampleParents.sel]->second / excl );
- TextDisabledUnformatted( buf );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
- ImGui::TextUnformatted( ICON_FA_AT " Frame location:" );
- ImGui::SameLine();
- ImGui::RadioButton( "Source code", &m_showCallstackFrameAddress, 0 );
- ImGui::SameLine();
- ImGui::RadioButton( "Entry point", &m_showCallstackFrameAddress, 3 );
- ImGui::SameLine();
- ImGui::RadioButton( "Return address", &m_showCallstackFrameAddress, 1 );
- ImGui::SameLine();
- ImGui::RadioButton( "Symbol address", &m_showCallstackFrameAddress, 2 );
- ImGui::PopStyleVar();
-
- auto& cs = m_worker.GetParentCallstack( data[m_sampleParents.sel]->first );
- ImGui::Separator();
- if( ImGui::BeginTable( "##callstack", 4, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY ) )
- {
- ImGui::TableSetupScrollFreeze( 0, 1 );
- ImGui::TableSetupColumn( "Frame", ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
- ImGui::TableSetupColumn( "Function" );
- ImGui::TableSetupColumn( "Location" );
- ImGui::TableSetupColumn( "Image" );
- ImGui::TableHeadersRow();
-
- int fidx = 0;
- int bidx = 0;
- for( auto& entry : cs )
- {
- auto frameData = entry.custom ? m_worker.GetParentCallstackFrame( entry ) : m_worker.GetCallstackFrame( entry );
- assert( frameData );
- const auto fsz = frameData->size;
- for( uint8_t f=0; fdata[f];
- auto txt = m_worker.GetString( frame.name );
- bidx++;
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
- if( f == fsz-1 )
- {
- ImGui::Text( "%i", fidx++ );
- }
- else
- {
- ImGui::PushFont( m_smallFont );
- TextDisabledUnformatted( "inline" );
- ImGui::PopFont();
- }
- ImGui::TableNextColumn();
- {
- ImGui::PushTextWrapPos( 0.0f );
- if( txt[0] == '[' )
- {
- TextDisabledUnformatted( txt );
- }
- else if( m_worker.GetCanonicalPointer( entry ) >> 63 != 0 )
- {
- TextColoredUnformatted( 0xFF8888FF, txt );
- }
- else
- {
- ImGui::TextUnformatted( txt );
- }
- ImGui::PopTextWrapPos();
- }
- if( ImGui::IsItemClicked() )
- {
- ImGui::SetClipboardText( txt );
- }
- ImGui::TableNextColumn();
- ImGui::PushTextWrapPos( 0.0f );
- float indentVal = 0.f;
- if( m_sampleParentBuzzAnim.Match( bidx ) )
- {
- const auto time = m_sampleParentBuzzAnim.Time();
- indentVal = sin( time * 60.f ) * 10.f * time;
- ImGui::Indent( indentVal );
- }
- txt = m_worker.GetString( frame.file );
- switch( m_showCallstackFrameAddress )
- {
- case 0:
- TextDisabledUnformatted( LocationToString( txt, frame.line ) );
- if( ImGui::IsItemClicked() )
- {
- ImGui::SetClipboardText( txt );
- }
- break;
- case 1:
- if( entry.custom == 0 )
- {
- const auto addr = m_worker.GetCanonicalPointer( entry );
- ImGui::TextDisabled( "0x%" PRIx64, addr );
- if( ImGui::IsItemClicked() )
- {
- char tmp[32];
- sprintf( tmp, "0x%" PRIx64, addr );
- ImGui::SetClipboardText( tmp );
- }
- }
- else
- {
- TextDisabledUnformatted( "unavailable" );
- }
- break;
- case 2:
- ImGui::TextDisabled( "0x%" PRIx64, frame.symAddr );
- if( ImGui::IsItemClicked() )
- {
- char tmp[32];
- sprintf( tmp, "0x%" PRIx64, frame.symAddr );
- ImGui::SetClipboardText( tmp );
- }
- break;
- case 3:
- {
- const auto sym = m_worker.GetSymbolData( frame.symAddr );
- if( sym )
- {
- const auto symtxt = m_worker.GetString( sym->file );
- TextDisabledUnformatted( LocationToString( symtxt, sym->line ) );
- if( ImGui::IsItemClicked() )
- {
- ImGui::SetClipboardText( symtxt );
- }
- }
- else
- {
- TextDisabledUnformatted( "[unknown]" );
- }
- break;
- }
- default:
- assert( false );
- break;
- }
- if( ImGui::IsItemHovered() )
- {
- if( m_showCallstackFrameAddress == 3 )
- {
- const auto sym = m_worker.GetSymbolData( frame.symAddr );
- if( sym )
- {
- const auto symtxt = m_worker.GetString( sym->file );
- DrawSourceTooltip( symtxt, sym->line );
- }
- }
- else
- {
- DrawSourceTooltip( txt, frame.line );
- }
- if( ImGui::IsItemClicked( 1 ) )
- {
- if( m_showCallstackFrameAddress == 3 )
- {
- const auto sym = m_worker.GetSymbolData( frame.symAddr );
- if( sym )
- {
- const auto symtxt = m_worker.GetString( sym->file );
- if( !ViewDispatch( symtxt, sym->line, frame.symAddr ) )
- {
- m_sampleParentBuzzAnim.Enable( bidx, 0.5f );
- }
- }
- else
- {
- m_sampleParentBuzzAnim.Enable( bidx, 0.5f );
- }
- }
- else
- {
- if( !ViewDispatch( txt, frame.line, frame.symAddr ) )
- {
- m_sampleParentBuzzAnim.Enable( bidx, 0.5f );
- }
- }
- }
- }
- if( indentVal != 0.f )
- {
- ImGui::Unindent( indentVal );
- }
- ImGui::PopTextWrapPos();
- ImGui::TableNextColumn();
- if( frameData->imageName.Active() )
- {
- TextDisabledUnformatted( m_worker.GetString( frameData->imageName ) );
- }
- }
- }
- ImGui::EndTable();
- }
- break;
- }
- case 1:
- {
- SmallCheckbox( "Group by function name", &m_sampleParents.groupBottomUp );
- auto tree = GetParentsCallstackFrameTreeBottomUp( stats, m_sampleParents.groupBottomUp );
- if( !tree.empty() )
- {
- int idx = 0;
- DrawParentsFrameTreeLevel( tree, idx );
- }
- else
- {
- TextDisabledUnformatted( "No call stacks to show" );
- }
-
- break;
- }
- case 2:
- {
- SmallCheckbox( "Group by function name", &m_sampleParents.groupTopDown );
- auto tree = GetParentsCallstackFrameTreeTopDown( stats, m_sampleParents.groupTopDown );
- if( !tree.empty() )
- {
- int idx = 0;
- DrawParentsFrameTreeLevel( tree, idx );
- }
- else
- {
- TextDisabledUnformatted( "No call stacks to show" );
- }
- break;
- }
- default:
- assert( false );
- break;
- }
- ImGui::EndChild();
- }
- ImGui::End();
-
- if( !show )
- {
- m_sampleParents.symAddr = 0;
- }
-}
-
void View::DrawRanges()
{
ImGui::Begin( "Time range limits", &m_showRanges, ImGuiWindowFlags_AlwaysAutoResize );
diff --git a/server/TracyView_Samples.cpp b/server/TracyView_Samples.cpp
new file mode 100644
index 00000000..4c96000a
--- /dev/null
+++ b/server/TracyView_Samples.cpp
@@ -0,0 +1,869 @@
+#include
+
+#include "TracyFilesystem.hpp"
+#include "TracyPrint.hpp"
+#include "TracySourceView.hpp"
+#include "TracyView.hpp"
+
+namespace tracy
+{
+
+void View::DrawSamplesStatistics( Vector& data, int64_t timeRange, AccumulationMode accumulationMode )
+{
+ static unordered_flat_map inlineMap;
+ assert( inlineMap.empty() );
+ if( !m_statSeparateInlines )
+ {
+ static unordered_flat_map baseMap;
+ assert( baseMap.empty() );
+ for( auto& v : data )
+ {
+ auto sym = m_worker.GetSymbolData( v.symAddr );
+ const auto symAddr = ( sym && sym->isInline ) ? m_worker.GetSymbolForAddress( v.symAddr ) : v.symAddr;
+ auto it = baseMap.find( symAddr );
+ if( it == baseMap.end() )
+ {
+ baseMap.emplace( symAddr, SymList { symAddr, v.incl, v.excl, 0 } );
+ }
+ else
+ {
+ assert( symAddr == it->second.symAddr );
+ it->second.incl += v.incl;
+ it->second.excl += v.excl;
+ it->second.count++;
+ }
+ }
+ for( auto& v : data ) inlineMap.emplace( v.symAddr, SymList { v.symAddr, v.incl, v.excl, v.count } );
+ data.clear();
+ for( auto& v : baseMap )
+ {
+ data.push_back_no_space_check( v.second );
+ }
+ baseMap.clear();
+ }
+
+ if( data.empty() )
+ {
+ ImGui::TextUnformatted( "No entries to be displayed." );
+ }
+ else
+ {
+ const auto& symMap = m_worker.GetSymbolMap();
+
+ if( accumulationMode == 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; } );
+ }
+ else
+ {
+ pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l.incl != r.incl ? l.incl > r.incl : l.symAddr < r.symAddr; } );
+ }
+
+ ImGui::BeginChild( "##statisticsSampling" );
+ if( ImGui::BeginTable( "##statisticsSampling", 5, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY ) )
+ {
+ ImGui::TableSetupScrollFreeze( 0, 1 );
+ ImGui::TableSetupColumn( "Name", ImGuiTableColumnFlags_NoHide );
+ ImGui::TableSetupColumn( "Location", ImGuiTableColumnFlags_NoSort );
+ ImGui::TableSetupColumn( "Image" );
+ ImGui::TableSetupColumn( m_statSampleTime ? "Time" : "Count", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
+ ImGui::TableSetupColumn( "Code size", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
+ ImGui::TableHeadersRow();
+
+ double revSampleCount100;
+ if( m_statRange.active && m_worker.GetSamplingPeriod() != 0 )
+ {
+ const auto st = m_statRange.max - m_statRange.min;
+ const auto cnt = st / m_worker.GetSamplingPeriod();
+ revSampleCount100 = 100. / cnt;
+ }
+ else
+ {
+ revSampleCount100 = 100. / m_worker.GetCallstackSampleCount();
+ }
+
+ const bool showAll = m_showAllSymbols;
+ const auto period = m_worker.GetSamplingPeriod();
+ int idx = 0;
+ for( auto& v : data )
+ {
+ const auto cnt = accumulationMode == AccumulationMode::SelfOnly ? v.excl : v.incl;
+ if( cnt > 0 || showAll )
+ {
+ const char* name = "[unknown]";
+ const char* file = "[unknown]";
+ const char* imageName = "[unknown]";
+ uint32_t line = 0;
+ bool isInline = false;
+ uint32_t symlen = 0;
+ auto codeAddr = v.symAddr;
+
+ auto sit = symMap.find( v.symAddr );
+ if( sit != symMap.end() )
+ {
+ name = m_worker.GetString( sit->second.name );
+ imageName = m_worker.GetString( sit->second.imageName );
+ isInline = sit->second.isInline;
+ switch( m_statSampleLocation )
+ {
+ case 0:
+ file = m_worker.GetString( sit->second.file );
+ line = sit->second.line;
+ break;
+ case 1:
+ file = m_worker.GetString( sit->second.callFile );
+ line = sit->second.callLine;
+ break;
+ case 2:
+ if( sit->second.isInline )
+ {
+ file = m_worker.GetString( sit->second.callFile );
+ line = sit->second.callLine;
+ }
+ else
+ {
+ file = m_worker.GetString( sit->second.file );
+ line = sit->second.line;
+ }
+ break;
+ default:
+ assert( false );
+ break;
+ }
+ if( m_statHideUnknown && file[0] == '[' ) continue;
+ symlen = sit->second.size.Val();
+ }
+ else if( m_statHideUnknown )
+ {
+ continue;
+ }
+
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+
+ const bool isKernel = v.symAddr >> 63 != 0;
+ const char* parentName = nullptr;
+ if( symlen == 0 && !isKernel )
+ {
+ const auto parentAddr = m_worker.GetSymbolForAddress( v.symAddr );
+ if( parentAddr != 0 )
+ {
+ auto pit = symMap.find( parentAddr );
+ if( pit != symMap.end() )
+ {
+ codeAddr = parentAddr;
+ symlen = pit->second.size.Val();
+ parentName = m_worker.GetString( pit->second.name );
+ }
+ }
+ }
+
+ bool expand = false;
+ if( !m_statSeparateInlines )
+ {
+ if( v.count > 0 && v.symAddr != 0 )
+ {
+ ImGui::PushID( v.symAddr );
+ expand = ImGui::TreeNodeEx( "", v.count == 0 ? ImGuiTreeNodeFlags_Leaf : 0 );
+ ImGui::PopID();
+ ImGui::SameLine();
+ }
+ }
+ else if( isInline )
+ {
+ TextDisabledUnformatted( ICON_FA_CARET_RIGHT );
+ ImGui::SameLine();
+ }
+ uint32_t excl;
+ if( m_statSeparateInlines )
+ {
+ excl = v.excl;
+ }
+ else
+ {
+ auto it = inlineMap.find( v.symAddr );
+ excl = it != inlineMap.end() ? it->second.excl : 0;
+ }
+ bool hasNoSamples = v.symAddr == 0 || excl == 0;
+ if( !m_statSeparateInlines && hasNoSamples && v.symAddr != 0 && v.count > 0 )
+ {
+ auto inSym = m_worker.GetInlineSymbolList( v.symAddr, symlen );
+ if( inSym )
+ {
+ const auto symEnd = v.symAddr + symlen;
+ while( *inSym < symEnd )
+ {
+ auto sit = inlineMap.find( *inSym );
+ if( sit != inlineMap.end() )
+ {
+ if( sit->second.excl != 0 )
+ {
+ hasNoSamples = false;
+ break;
+ }
+ }
+ inSym++;
+ }
+ }
+ }
+ if( hasNoSamples )
+ {
+ if( isKernel )
+ {
+ TextColoredUnformatted( 0xFF8888FF, name );
+ }
+ else
+ {
+ ImGui::TextUnformatted( name );
+ }
+ }
+ else
+ {
+ ImGui::PushID( idx++ );
+ if( isKernel ) ImGui::PushStyleColor( ImGuiCol_Text, 0xFF8888FF );
+ const auto clicked = ImGui::Selectable( name, m_sampleParents.withInlines && m_sampleParents.symAddr == v.symAddr, ImGuiSelectableFlags_SpanAllColumns );
+ if( isKernel ) ImGui::PopStyleColor();
+ if( clicked ) ShowSampleParents( v.symAddr, !m_statSeparateInlines );
+ ImGui::PopID();
+ }
+ if( parentName )
+ {
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", parentName );
+ }
+ if( !m_statSeparateInlines && v.count > 0 && v.symAddr != 0 )
+ {
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(+%s)", RealToString( v.count ) );
+ }
+ ImGui::TableNextColumn();
+ float indentVal = 0.f;
+ if( m_statBuzzAnim.Match( v.symAddr ) )
+ {
+ const auto time = m_statBuzzAnim.Time();
+ indentVal = sin( time * 60.f ) * 10.f * time;
+ ImGui::Indent( indentVal );
+ }
+ if( m_statShowAddress )
+ {
+ ImGui::TextDisabled( "0x%" PRIx64, v.symAddr );
+ }
+ else
+ {
+ TextDisabledUnformatted( LocationToString( file, line ) );
+ }
+ if( ImGui::IsItemHovered() )
+ {
+ DrawSourceTooltip( file, line );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ if( SourceFileValid( file, m_worker.GetCaptureTime(), *this, m_worker ) )
+ {
+ ViewSymbol( file, line, codeAddr, v.symAddr );
+ if( !m_statSeparateInlines ) m_sourceView->CalcInlineStats( false );
+ }
+ else if( symlen != 0 )
+ {
+ uint32_t len;
+ if( m_worker.GetSymbolCode( codeAddr, len ) )
+ {
+ ViewSymbol( nullptr, 0, codeAddr, v.symAddr );
+ if( !m_statSeparateInlines ) m_sourceView->CalcInlineStats( false );
+ }
+ else
+ {
+ m_statBuzzAnim.Enable( v.symAddr, 0.5f );
+ }
+ }
+ else
+ {
+ m_statBuzzAnim.Enable( v.symAddr, 0.5f );
+ }
+ }
+ }
+ if( indentVal != 0.f )
+ {
+ ImGui::Unindent( indentVal );
+ }
+ ImGui::TableNextColumn();
+ TextDisabledUnformatted( imageName );
+ ImGui::TableNextColumn();
+ if( cnt > 0 )
+ {
+ char buf[64];
+ if( m_statSampleTime )
+ {
+ const auto t = cnt * period;
+ ImGui::TextUnformatted( TimeToString( t ) );
+ PrintStringPercent( buf, 100. * t / timeRange );
+ }
+ else
+ {
+ ImGui::TextUnformatted( RealToString( cnt ) );
+ PrintStringPercent( buf, cnt * revSampleCount100 );
+ }
+ ImGui::SameLine();
+ TextDisabledUnformatted( buf );
+ }
+ ImGui::TableNextColumn();
+ if( symlen != 0 )
+ {
+ if( m_worker.HasSymbolCode( codeAddr ) )
+ {
+ TextDisabledUnformatted( ICON_FA_DATABASE );
+ ImGui::SameLine();
+ }
+ if( isInline )
+ {
+ TextDisabledUnformatted( "<" );
+ ImGui::SameLine();
+ }
+ TextDisabledUnformatted( MemSizeToString( symlen ) );
+ }
+
+ if( !m_statSeparateInlines && expand )
+ {
+ assert( v.count > 0 );
+ assert( symlen != 0 );
+ auto inSym = m_worker.GetInlineSymbolList( v.symAddr, symlen );
+ assert( inSym != nullptr );
+ const auto symEnd = v.symAddr + symlen;
+ Vector inSymList;
+ while( *inSym < symEnd )
+ {
+ auto sit = inlineMap.find( *inSym );
+ if( sit != inlineMap.end() )
+ {
+ inSymList.push_back( SymList { *inSym, sit->second.incl, sit->second.excl } );
+ }
+ else
+ {
+ inSymList.push_back( SymList { *inSym, 0, 0 } );
+ }
+ inSym++;
+ }
+ auto statIt = inlineMap.find( v.symAddr );
+ if( statIt != inlineMap.end() )
+ {
+ inSymList.push_back( SymList { v.symAddr, statIt->second.incl, statIt->second.excl } );
+ }
+
+ if( accumulationMode == 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; } );
+ }
+ else
+ {
+ pdqsort_branchless( inSymList.begin(), inSymList.end(), []( const auto& l, const auto& r ) { return l.incl != l.incl ? l.incl > r.incl : l.symAddr < r.symAddr; } );
+ }
+
+ ImGui::Indent();
+ for( auto& iv : inSymList )
+ {
+ const auto cnt = accumulationMode == AccumulationMode::SelfOnly ? iv.excl : iv.incl;
+ if( cnt > 0 || showAll )
+ {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ auto sit = symMap.find( iv.symAddr );
+ assert( sit != symMap.end() );
+ name = m_worker.GetString( sit->second.name );
+ switch( m_statSampleLocation )
+ {
+ case 0:
+ file = m_worker.GetString( sit->second.file );
+ line = sit->second.line;
+ break;
+ case 1:
+ file = m_worker.GetString( sit->second.callFile );
+ line = sit->second.callLine;
+ break;
+ case 2:
+ if( sit->second.isInline )
+ {
+ file = m_worker.GetString( sit->second.callFile );
+ line = sit->second.callLine;
+ }
+ else
+ {
+ file = m_worker.GetString( sit->second.file );
+ line = sit->second.line;
+ }
+ break;
+ default:
+ assert( false );
+ break;
+ }
+
+ const auto sn = iv.symAddr == v.symAddr ? "[ - self - ]" : name;
+ if( iv.excl == 0 )
+ {
+ ImGui::TextUnformatted( sn );
+ }
+ else
+ {
+ ImGui::PushID( idx++ );
+ if( ImGui::Selectable( sn, !m_sampleParents.withInlines && m_sampleParents.symAddr == iv.symAddr, ImGuiSelectableFlags_SpanAllColumns ) )
+ {
+ ShowSampleParents( iv.symAddr, false );
+ }
+ ImGui::PopID();
+ }
+ ImGui::TableNextColumn();
+ float indentVal = 0.f;
+ if( m_statBuzzAnim.Match( iv.symAddr ) )
+ {
+ const auto time = m_statBuzzAnim.Time();
+ indentVal = sin( time * 60.f ) * 10.f * time;
+ ImGui::Indent( indentVal );
+ }
+ if( m_statShowAddress )
+ {
+ ImGui::TextDisabled( "0x%" PRIx64, iv.symAddr );
+ }
+ else
+ {
+ TextDisabledUnformatted( LocationToString( file, line ) );
+ }
+ if( ImGui::IsItemHovered() )
+ {
+ DrawSourceTooltip( file, line );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ if( SourceFileValid( file, m_worker.GetCaptureTime(), *this, m_worker ) )
+ {
+ ViewSymbol( file, line, codeAddr, iv.symAddr );
+ if( !m_statSeparateInlines ) m_sourceView->CalcInlineStats( true );
+ }
+ else if( symlen != 0 )
+ {
+ uint32_t len;
+ if( m_worker.GetSymbolCode( codeAddr, len ) )
+ {
+ ViewSymbol( nullptr, 0, codeAddr, iv.symAddr );
+ if( !m_statSeparateInlines ) m_sourceView->CalcInlineStats( true );
+ }
+ else
+ {
+ m_statBuzzAnim.Enable( iv.symAddr, 0.5f );
+ }
+ }
+ else
+ {
+ m_statBuzzAnim.Enable( iv.symAddr, 0.5f );
+ }
+ }
+ }
+ if( indentVal != 0.f )
+ {
+ ImGui::Unindent( indentVal );
+ }
+ ImGui::TableNextColumn();
+ ImGui::TableNextColumn();
+ if( cnt > 0 )
+ {
+ char buf[64];
+ if( m_statSampleTime )
+ {
+ const auto t = cnt * period;
+ ImGui::TextUnformatted( TimeToString( t ) );
+ PrintStringPercent( buf, 100. * t / timeRange );
+ }
+ else
+ {
+ ImGui::TextUnformatted( RealToString( cnt ) );
+ PrintStringPercent( buf, cnt * revSampleCount100 );
+ }
+ ImGui::SameLine();
+ TextDisabledUnformatted( buf );
+ }
+ }
+ }
+ ImGui::Unindent();
+ ImGui::TreePop();
+ }
+ }
+ }
+ ImGui::EndTable();
+ }
+ ImGui::EndChild();
+
+ inlineMap.clear();
+ }
+}
+
+void View::DrawSampleParents()
+{
+ bool show = true;
+ const auto scale = GetScale();
+ ImGui::SetNextWindowSize( ImVec2( 1400 * scale, 500 * scale ), ImGuiCond_FirstUseEver );
+ ImGui::Begin( "Sample entry call stacks", &show, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
+ if( !ImGui::GetCurrentWindowRead()->SkipItems )
+ {
+ auto ss = m_worker.GetSymbolStats( m_sampleParents.symAddr );
+ auto excl = ss->excl;
+ auto stats = ss->parents;
+
+ const auto symbol = m_worker.GetSymbolData( m_sampleParents.symAddr );
+ if( !symbol->isInline && m_sampleParents.withInlines )
+ {
+ const auto symlen = symbol->size.Val();
+ auto inSym = m_worker.GetInlineSymbolList( m_sampleParents.symAddr, symlen );
+ if( inSym )
+ {
+ const auto symEnd = m_sampleParents.symAddr + symlen;
+ while( *inSym < symEnd )
+ {
+ auto istat = m_worker.GetSymbolStats( *inSym++ );
+ if( !istat ) continue;
+ excl += istat->excl;
+ for( auto& v : istat->baseParents )
+ {
+ auto it = stats.find( v.first );
+ if( it == stats.end() )
+ {
+ stats.emplace( v.first, v.second );
+ }
+ else
+ {
+ it->second += v.second;
+ }
+ }
+ }
+ }
+ }
+ assert( !stats.empty() );
+
+ ImGui::PushFont( m_bigFont );
+ TextFocused( "Symbol:", m_worker.GetString( symbol->name ) );
+ if( symbol->isInline )
+ {
+ ImGui::SameLine();
+ TextDisabledUnformatted( "(inline)" );
+ }
+ else if( !m_sampleParents.withInlines )
+ {
+ ImGui::SameLine();
+ TextDisabledUnformatted( "(without inlines)" );
+ }
+ ImGui::PopFont();
+ TextDisabledUnformatted( "Location:" );
+ ImGui::SameLine();
+ const auto callFile = m_worker.GetString( symbol->callFile );
+ ImGui::TextUnformatted( LocationToString( callFile, symbol->callLine ) );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ ViewDispatch( callFile, symbol->callLine, m_sampleParents.symAddr );
+ }
+ TextDisabledUnformatted( "Entry point:" );
+ ImGui::SameLine();
+ const auto file = m_worker.GetString( symbol->file );
+ ImGui::TextUnformatted( LocationToString( file, symbol->line ) );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ ViewDispatch( file, symbol->line, m_sampleParents.symAddr );
+ }
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ TextDisabledUnformatted( m_worker.GetString( symbol->imageName ) );
+ ImGui::Separator();
+ ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 2, 2 ) );
+ if( ImGui::RadioButton( ICON_FA_TABLE " List", m_sampleParents.mode == 0 ) ) m_sampleParents.mode = 0;
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ if( ImGui::RadioButton( ICON_FA_TREE " Bottom-up tree", m_sampleParents.mode == 1 ) ) m_sampleParents.mode = 1;
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ if( ImGui::RadioButton( ICON_FA_TREE " Top-down tree", m_sampleParents.mode == 2 ) ) m_sampleParents.mode = 2;
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::SeparatorEx( ImGuiSeparatorFlags_Vertical );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::Checkbox( ICON_FA_STOPWATCH " Show time", &m_statSampleTime );
+ ImGui::PopStyleVar();
+ ImGui::Separator();
+ ImGui::BeginChild( "##sampleParents" );
+ switch( m_sampleParents.mode )
+ {
+ case 0:
+ {
+ TextDisabledUnformatted( "Entry call stack:" );
+ ImGui::SameLine();
+ if( ImGui::SmallButton( " " ICON_FA_CARET_LEFT " " ) )
+ {
+ m_sampleParents.sel = std::max( m_sampleParents.sel - 1, 0 );
+ }
+ ImGui::SameLine();
+ ImGui::Text( "%s / %s", RealToString( m_sampleParents.sel + 1 ), RealToString( stats.size() ) );
+ if( ImGui::IsItemClicked() ) ImGui::OpenPopup( "EntryCallStackPopup" );
+ ImGui::SameLine();
+ if( ImGui::SmallButton( " " ICON_FA_CARET_RIGHT " " ) )
+ {
+ m_sampleParents.sel = std::min( m_sampleParents.sel + 1, stats.size() - 1 );
+ }
+ if( ImGui::BeginPopup( "EntryCallStackPopup" ) )
+ {
+ int sel = m_sampleParents.sel + 1;
+ ImGui::SetNextItemWidth( 120 * scale );
+ const bool clicked = ImGui::InputInt( "##entryCallStack", &sel, 1, 100, ImGuiInputTextFlags_EnterReturnsTrue );
+ if( clicked ) m_sampleParents.sel = std::min( std::max( sel, 1 ), int( stats.size() ) ) - 1;
+ ImGui::EndPopup();
+ }
+ Vector data;
+ data.reserve( stats.size() );
+ for( auto it = stats.begin(); it != stats.end(); ++it ) data.push_back( it );
+ pdqsort_branchless( data.begin(), data.end(), []( const auto& l, const auto& r ) { return l->second > r->second; } );
+ ImGui::SameLine();
+ ImGui::TextUnformatted( m_statSampleTime ? TimeToString( m_worker.GetSamplingPeriod() * data[m_sampleParents.sel]->second ) : RealToString( data[m_sampleParents.sel]->second ) );
+ ImGui::SameLine();
+ char buf[64];
+ PrintStringPercent( buf, 100. * data[m_sampleParents.sel]->second / excl );
+ TextDisabledUnformatted( buf );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) );
+ ImGui::TextUnformatted( ICON_FA_AT " Frame location:" );
+ ImGui::SameLine();
+ ImGui::RadioButton( "Source code", &m_showCallstackFrameAddress, 0 );
+ ImGui::SameLine();
+ ImGui::RadioButton( "Entry point", &m_showCallstackFrameAddress, 3 );
+ ImGui::SameLine();
+ ImGui::RadioButton( "Return address", &m_showCallstackFrameAddress, 1 );
+ ImGui::SameLine();
+ ImGui::RadioButton( "Symbol address", &m_showCallstackFrameAddress, 2 );
+ ImGui::PopStyleVar();
+
+ auto& cs = m_worker.GetParentCallstack( data[m_sampleParents.sel]->first );
+ ImGui::Separator();
+ if( ImGui::BeginTable( "##callstack", 4, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Borders | ImGuiTableFlags_ScrollY ) )
+ {
+ ImGui::TableSetupScrollFreeze( 0, 1 );
+ ImGui::TableSetupColumn( "Frame", ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
+ ImGui::TableSetupColumn( "Function" );
+ ImGui::TableSetupColumn( "Location" );
+ ImGui::TableSetupColumn( "Image" );
+ ImGui::TableHeadersRow();
+
+ int fidx = 0;
+ int bidx = 0;
+ for( auto& entry : cs )
+ {
+ auto frameData = entry.custom ? m_worker.GetParentCallstackFrame( entry ) : m_worker.GetCallstackFrame( entry );
+ assert( frameData );
+ const auto fsz = frameData->size;
+ for( uint8_t f=0; fdata[f];
+ auto txt = m_worker.GetString( frame.name );
+ bidx++;
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+ if( f == fsz-1 )
+ {
+ ImGui::Text( "%i", fidx++ );
+ }
+ else
+ {
+ ImGui::PushFont( m_smallFont );
+ TextDisabledUnformatted( "inline" );
+ ImGui::PopFont();
+ }
+ ImGui::TableNextColumn();
+ {
+ ImGui::PushTextWrapPos( 0.0f );
+ if( txt[0] == '[' )
+ {
+ TextDisabledUnformatted( txt );
+ }
+ else if( m_worker.GetCanonicalPointer( entry ) >> 63 != 0 )
+ {
+ TextColoredUnformatted( 0xFF8888FF, txt );
+ }
+ else
+ {
+ ImGui::TextUnformatted( txt );
+ }
+ ImGui::PopTextWrapPos();
+ }
+ if( ImGui::IsItemClicked() )
+ {
+ ImGui::SetClipboardText( txt );
+ }
+ ImGui::TableNextColumn();
+ ImGui::PushTextWrapPos( 0.0f );
+ float indentVal = 0.f;
+ if( m_sampleParentBuzzAnim.Match( bidx ) )
+ {
+ const auto time = m_sampleParentBuzzAnim.Time();
+ indentVal = sin( time * 60.f ) * 10.f * time;
+ ImGui::Indent( indentVal );
+ }
+ txt = m_worker.GetString( frame.file );
+ switch( m_showCallstackFrameAddress )
+ {
+ case 0:
+ TextDisabledUnformatted( LocationToString( txt, frame.line ) );
+ if( ImGui::IsItemClicked() )
+ {
+ ImGui::SetClipboardText( txt );
+ }
+ break;
+ case 1:
+ if( entry.custom == 0 )
+ {
+ const auto addr = m_worker.GetCanonicalPointer( entry );
+ ImGui::TextDisabled( "0x%" PRIx64, addr );
+ if( ImGui::IsItemClicked() )
+ {
+ char tmp[32];
+ sprintf( tmp, "0x%" PRIx64, addr );
+ ImGui::SetClipboardText( tmp );
+ }
+ }
+ else
+ {
+ TextDisabledUnformatted( "unavailable" );
+ }
+ break;
+ case 2:
+ ImGui::TextDisabled( "0x%" PRIx64, frame.symAddr );
+ if( ImGui::IsItemClicked() )
+ {
+ char tmp[32];
+ sprintf( tmp, "0x%" PRIx64, frame.symAddr );
+ ImGui::SetClipboardText( tmp );
+ }
+ break;
+ case 3:
+ {
+ const auto sym = m_worker.GetSymbolData( frame.symAddr );
+ if( sym )
+ {
+ const auto symtxt = m_worker.GetString( sym->file );
+ TextDisabledUnformatted( LocationToString( symtxt, sym->line ) );
+ if( ImGui::IsItemClicked() )
+ {
+ ImGui::SetClipboardText( symtxt );
+ }
+ }
+ else
+ {
+ TextDisabledUnformatted( "[unknown]" );
+ }
+ break;
+ }
+ default:
+ assert( false );
+ break;
+ }
+ if( ImGui::IsItemHovered() )
+ {
+ if( m_showCallstackFrameAddress == 3 )
+ {
+ const auto sym = m_worker.GetSymbolData( frame.symAddr );
+ if( sym )
+ {
+ const auto symtxt = m_worker.GetString( sym->file );
+ DrawSourceTooltip( symtxt, sym->line );
+ }
+ }
+ else
+ {
+ DrawSourceTooltip( txt, frame.line );
+ }
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ if( m_showCallstackFrameAddress == 3 )
+ {
+ const auto sym = m_worker.GetSymbolData( frame.symAddr );
+ if( sym )
+ {
+ const auto symtxt = m_worker.GetString( sym->file );
+ if( !ViewDispatch( symtxt, sym->line, frame.symAddr ) )
+ {
+ m_sampleParentBuzzAnim.Enable( bidx, 0.5f );
+ }
+ }
+ else
+ {
+ m_sampleParentBuzzAnim.Enable( bidx, 0.5f );
+ }
+ }
+ else
+ {
+ if( !ViewDispatch( txt, frame.line, frame.symAddr ) )
+ {
+ m_sampleParentBuzzAnim.Enable( bidx, 0.5f );
+ }
+ }
+ }
+ }
+ if( indentVal != 0.f )
+ {
+ ImGui::Unindent( indentVal );
+ }
+ ImGui::PopTextWrapPos();
+ ImGui::TableNextColumn();
+ if( frameData->imageName.Active() )
+ {
+ TextDisabledUnformatted( m_worker.GetString( frameData->imageName ) );
+ }
+ }
+ }
+ ImGui::EndTable();
+ }
+ break;
+ }
+ case 1:
+ {
+ SmallCheckbox( "Group by function name", &m_sampleParents.groupBottomUp );
+ auto tree = GetParentsCallstackFrameTreeBottomUp( stats, m_sampleParents.groupBottomUp );
+ if( !tree.empty() )
+ {
+ int idx = 0;
+ DrawParentsFrameTreeLevel( tree, idx );
+ }
+ else
+ {
+ TextDisabledUnformatted( "No call stacks to show" );
+ }
+
+ break;
+ }
+ case 2:
+ {
+ SmallCheckbox( "Group by function name", &m_sampleParents.groupTopDown );
+ auto tree = GetParentsCallstackFrameTreeTopDown( stats, m_sampleParents.groupTopDown );
+ if( !tree.empty() )
+ {
+ int idx = 0;
+ DrawParentsFrameTreeLevel( tree, idx );
+ }
+ else
+ {
+ TextDisabledUnformatted( "No call stacks to show" );
+ }
+ break;
+ }
+ default:
+ assert( false );
+ break;
+ }
+ ImGui::EndChild();
+ }
+ ImGui::End();
+
+ if( !show )
+ {
+ m_sampleParents.symAddr = 0;
+ }
+}
+
+}