diff --git a/server/TracyView.cpp b/server/TracyView.cpp index a0471528..1f755bf8 100644 --- a/server/TracyView.cpp +++ b/server/TracyView.cpp @@ -3568,18 +3568,16 @@ int View::DrawGhostLevel( const Vector& vec, bool hover, double pxns, } else { - const auto& ghostFrame = m_worker.GetGhostFrame( ev.frame ); - const auto frame = m_worker.GetCallstackFrame( ghostFrame ); + const auto& ghostKey = m_worker.GetGhostFrame( ev.frame ); + const auto frame = m_worker.GetCallstackFrame( ghostKey.frame ); const auto pr0 = ( ev.start.Val() - m_vd.zvStart ) * pxns; const auto pr1 = ( ev.end.Val() - m_vd.zvStart ) * pxns; const auto px0 = std::max( pr0, -10.0 ); const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } ); - int fsz; if( !frame ) { - fsz = 1; char symName[64]; - sprintf( symName, "0x%" PRIx64, m_worker.GetCanonicalPointer( ghostFrame ) ); + sprintf( symName, "0x%" PRIx64, m_worker.GetCanonicalPointer( ghostKey.frame ) ); const auto tsz = ImGui::CalcTextSize( symName ); const auto txtColor = 0xFF888888; @@ -3631,99 +3629,82 @@ int View::DrawGhostLevel( const Vector& vec, bool hover, double pxns, } else { - fsz = int( frame->size ); - auto foff = offset; - for( int i=fsz-1; i>=0; i-- ) + const auto& sym = frame->data[ghostKey.inlineFrame]; + const auto isInline = ghostKey.inlineFrame != frame->size-1; + const auto col = isInline ? DarkenColor( color ) : color; + const auto symName = m_worker.GetString( sym.name ); + uint32_t txtColor = symName[0] == '[' ? 0xFF999999 : 0xFFFFFFFF; + const auto tsz = ImGui::CalcTextSize( symName ); + + draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), col ); + draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), outline, 0.f, -1 ); + + if( tsz.x < zsz ) { - const auto isInline = i != fsz-1; - const auto col = isInline ? DarkenColor( color ) : color; - const auto sym = m_worker.GetSymbolData( frame->data[i].symAddr ); - const auto symName = m_worker.GetString( frame->data[i].name ); - uint32_t txtColor = symName[0] == '[' ? 0xFF999999 : 0xFFFFFFFF; - const auto tsz = ImGui::CalcTextSize( symName ); - - draw->AddRectFilled( wpos + ImVec2( px0, foff ), wpos + ImVec2( px1, foff + tsz.y ), col ); - draw->AddRect( wpos + ImVec2( px0, foff ), wpos + ImVec2( px1, foff + tsz.y ), outline, 0.f, -1 ); - - if( tsz.x < zsz ) + const auto x = ( ev.start.Val() - m_vd.zvStart ) * pxns + ( ( end - ev.start.Val() ) * pxns - tsz.x ) / 2; + if( x < 0 || x > w - tsz.x ) { - const auto x = ( ev.start.Val() - m_vd.zvStart ) * pxns + ( ( end - ev.start.Val() ) * pxns - tsz.x ) / 2; - if( x < 0 || x > w - tsz.x ) - { - ImGui::PushClipRect( wpos + ImVec2( px0, foff ), wpos + ImVec2( px1, foff + tsz.y * 2 ), true ); - DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), foff ), txtColor, symName ); - ImGui::PopClipRect(); - } - else if( ev.start.Val() == ev.end.Val() ) - { - DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, foff ), txtColor, symName ); - } - else - { - DrawTextContrast( draw, wpos + ImVec2( x, foff ), txtColor, symName ); - } + ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true ); + DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), txtColor, symName ); + ImGui::PopClipRect(); + } + else if( ev.start.Val() == ev.end.Val() ) + { + DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), txtColor, symName ); } else { - ImGui::PushClipRect( wpos + ImVec2( px0, foff ), wpos + ImVec2( px1, foff + tsz.y * 2 ), true ); - DrawTextContrast( draw, wpos + ImVec2( ( ev.start.Val() - m_vd.zvStart ) * pxns, foff ), txtColor, symName ); - ImGui::PopClipRect(); + DrawTextContrast( draw, wpos + ImVec2( x, offset ), txtColor, symName ); } + } + else + { + ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true ); + DrawTextContrast( draw, wpos + ImVec2( ( ev.start.Val() - m_vd.zvStart ) * pxns, offset ), txtColor, symName ); + ImGui::PopClipRect(); + } - if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, foff ), wpos + ImVec2( px1, foff + tsz.y ) ) ) + if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ) ) ) + { + ImGui::BeginTooltip(); + TextDisabledUnformatted( ICON_FA_GHOST " Ghost zone" ); + ImGui::Separator(); + ImGui::TextUnformatted( symName ); + if( isInline ) { - ImGui::BeginTooltip(); - TextDisabledUnformatted( ICON_FA_GHOST " Ghost zone" ); - ImGui::Separator(); - ImGui::TextUnformatted( symName ); - if( isInline ) - { - ImGui::SameLine(); - TextDisabledUnformatted( "[inline]" ); - } - if( sym ) TextFocused( "Image:", m_worker.GetString( sym->imageName ) ); - TextDisabledUnformatted( "Location:" ); ImGui::SameLine(); - const char* file; - uint32_t line; - if( sym && !isInline ) - { - file = m_worker.GetString( sym->file ); - line = sym->line; - } - else - { - file = m_worker.GetString( frame->data[i].file ); - line = frame->data[i].line; - } - ImGui::Text( "%s:%i", file, line ); - ImGui::SameLine(); - ImGui::TextDisabled( "(0x%" PRIx64 ")", m_worker.GetCanonicalPointer( ghostFrame ) ); - TextFocused( "Thread:", m_worker.GetThreadName( tid ) ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%s)", RealToString( tid ) ); - ImGui::Separator(); - TextFocused( "Execution time:", TimeToString( ev.end.Val() - ev.start.Val() ) ); - ImGui::EndTooltip(); - - if( ImGui::IsMouseClicked( 0 ) ) - { - ViewDispatch( file, line, frame->data[i].symAddr ); - } - else if( !m_zoomAnim.active && ImGui::IsMouseClicked( 2 ) ) - { - ZoomToRange( ev.start.Val(), ev.end.Val() ); - } + TextDisabledUnformatted( "[inline]" ); } + const auto symbol = m_worker.GetSymbolData( sym.symAddr ); + if( symbol ) TextFocused( "Image:", m_worker.GetString( symbol->imageName ) ); + TextDisabledUnformatted( "Location:" ); + ImGui::SameLine(); + const char* file = m_worker.GetString( sym.file ); + uint32_t line = sym.line; + ImGui::Text( "%s:%i", file, line ); + ImGui::SameLine(); + ImGui::TextDisabled( "(0x%" PRIx64 ")", sym.symAddr ); + TextFocused( "Thread:", m_worker.GetThreadName( tid ) ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( tid ) ); + ImGui::Separator(); + TextFocused( "Execution time:", TimeToString( ev.end.Val() - ev.start.Val() ) ); + ImGui::EndTooltip(); - foff += ostep; + if( ImGui::IsMouseClicked( 0 ) ) + { + ViewDispatch( file, line, sym.symAddr ); + } + else if( !m_zoomAnim.active && ImGui::IsMouseClicked( 2 ) ) + { + ZoomToRange( ev.start.Val(), ev.end.Val() ); + } } } - maxdepth = std::max( maxdepth, depth + fsz - 1 ); if( ev.child >= 0 ) { - const auto d = DispatchGhostLevel( m_worker.GetGhostChildren( ev.child ), hover, pxns, nspx, wpos, _offset, depth + fsz - 1, yMin, yMax, tid ); + const auto d = DispatchGhostLevel( m_worker.GetGhostChildren( ev.child ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid ); if( d > maxdepth ) maxdepth = d; } ++it; @@ -3770,13 +3751,9 @@ int View::SkipGhostLevel( const Vector& vec, bool hover, double pxns, } else { - const auto& ghostFrame = m_worker.GetGhostFrame( ev.frame ); - const auto frame = m_worker.GetCallstackFrame( ghostFrame ); - const auto fsz = frame ? int( frame->size ) : 1; - maxdepth = std::max( maxdepth, depth + fsz - 1 ); if( ev.child >= 0 ) { - const auto d = DispatchGhostLevel( m_worker.GetGhostChildren( ev.child ), hover, pxns, nspx, wpos, _offset, depth + fsz - 1, yMin, yMax, tid ); + const auto d = DispatchGhostLevel( m_worker.GetGhostChildren( ev.child ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid ); if( d > maxdepth ) maxdepth = d; } ++it; diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 92c88603..3dfc6d0d 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -4016,91 +4016,91 @@ void Worker::HandlePostponedSamples() while( it != m_data.postponedSamples.end() ); } -int Worker::AddGhostZone( const VarArray& cs, Vector* vec, uint64_t t ) +void Worker::GetStackWithInlines( Vector& ret, const VarArray& cs ) { - int gcnt = 0; + ret.clear(); int idx = cs.size() - 1; do { auto& entry = cs[idx]; + const auto frame = GetCallstackFrame( entry ); + if( frame ) + { + uint8_t i = frame->size; + do + { + i--; + ret.push_back( InlineStackData { frame->data[i].symAddr, entry, i } ); + } + while( i != 0 ); + } + else + { + ret.push_back( InlineStackData{ GetCanonicalPointer( entry ), entry, 0 } ); + } + } + while( idx-- > 0 ); +} + +int Worker::AddGhostZone( const VarArray& cs, Vector* vec, uint64_t t ) +{ + static Vector stack; + GetStackWithInlines( stack, cs ); + + const uint64_t refBackTime = vec->empty() ? 0 : vec->back().end.Val(); + int gcnt = 0; + int idx = 0; + while( !vec->empty() && idx < stack.size() ) + { + auto& back = vec->back(); + const auto& backKey = m_data.ghostFrames[back.frame.Val()]; + const auto backFrame = GetCallstackFrame( backKey.frame ); + if( !backFrame ) break; + const auto& inlineFrame = backFrame->data[backKey.inlineFrame]; + if( inlineFrame.symAddr != stack[idx].symAddr ) break; + if( back.end.Val() != refBackTime ) break; + back.end.SetVal( t + m_samplingPeriod ); + idx++; + if( back.child < 0 ) + { + back.child = m_data.ghostChildren.size(); + vec = &m_data.ghostChildren.push_next(); + } + else + { + vec = &m_data.ghostChildren[back.child]; + } + } + while( idx < stack.size() ) + { + gcnt++; uint32_t fid; - auto it = m_data.ghostFramesMap.find( entry.data ); + GhostKey key { stack[idx].frame, stack[idx].inlineFrame }; + auto it = m_data.ghostFramesMap.find( key ); if( it == m_data.ghostFramesMap.end() ) { fid = uint32_t( m_data.ghostFrames.size() ); - m_data.ghostFrames.push_back( entry ); - m_data.ghostFramesMap.emplace( entry.data, fid ); + m_data.ghostFrames.push_back( key ); + m_data.ghostFramesMap.emplace( key, fid ); } else { fid = it->second; } - if( vec->empty() ) + auto& zone = vec->push_next(); + zone.start.SetVal( t ); + zone.end.SetVal( t + m_samplingPeriod ); + zone.frame.SetVal( fid ); + if( ++idx == stack.size() ) { - gcnt++; - auto& zone = vec->push_next(); - zone.start.SetVal( t ); - zone.end.SetVal( t + m_samplingPeriod ); - zone.frame.SetVal( fid ); zone.child = -1; } else { - auto& back = vec->back(); - const auto backFrame = GetCallstackFrame( m_data.ghostFrames[back.frame.Val()] ); - const auto thisFrame = GetCallstackFrame( entry ); - bool match = false; - if( backFrame && thisFrame ) - { - match = backFrame->size == thisFrame->size; - if( match ) - { - for( uint8_t i=0; isize; i++ ) - { - if( backFrame->data[i].symAddr != thisFrame->data[i].symAddr ) - { - match = false; - break; - } - } - } - } - if( match ) - { - back.end.SetVal( t + m_samplingPeriod ); - } - else - { - gcnt++; - auto ptr = &back; - for(;;) - { - ptr->end.SetVal( t ); - if( ptr->child < 0 ) break; - ptr = &GetGhostChildrenMutable( ptr->child ).back(); - } - auto& zone = vec->push_next_non_empty(); - zone.start.SetVal( t ); - zone.end.SetVal( t + m_samplingPeriod ); - zone.frame.SetVal( fid ); - zone.child = -1; - } - } - if( idx > 0 ) - { - auto& zone = vec->back(); - if( zone.child < 0 ) - { - zone.child = m_data.ghostChildren.size(); - vec = &m_data.ghostChildren.push_next(); - } - else - { - vec = &m_data.ghostChildren[zone.child]; - } + zone.child = m_data.ghostChildren.size(); + vec = &m_data.ghostChildren.push_next(); } } - while( idx-- > 0 ); return gcnt; } diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 491befc9..652565fe 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -130,6 +130,37 @@ public: uint32_t len; }; + struct InlineStackData + { + uint64_t symAddr; + CallstackFrameId frame; + uint8_t inlineFrame; + }; + +#pragma pack( 1 ) + struct GhostKey + { + CallstackFrameId frame; + uint8_t inlineFrame; + }; +#pragma pack() + + struct GhostKeyHasher + { + size_t operator()( const GhostKey& key ) const + { + return charutil::hash( (const char*)&key, sizeof( GhostKey ) ); + } + }; + + struct GhostKeyComparator + { + bool operator()( const GhostKey& lhs, const GhostKey& rhs ) const + { + return memcmp( &lhs, &rhs, sizeof( GhostKey ) ) == 0; + } + }; + private: struct SourceLocationZones { @@ -265,8 +296,8 @@ private: Vector>> gpuChildren; #ifndef TRACY_NO_STATISTICS Vector> ghostChildren; - Vector ghostFrames; - unordered_flat_map ghostFramesMap; + Vector ghostFrames; + unordered_flat_map ghostFramesMap; #endif Vector>> zoneVectorCache; @@ -493,7 +524,7 @@ public: tracy_force_inline const Vector>& GetGpuChildren( int32_t idx ) const { return m_data.gpuChildren[idx]; } #ifndef TRACY_NO_STATISTICS tracy_force_inline const Vector& GetGhostChildren( int32_t idx ) const { return m_data.ghostChildren[idx]; } - tracy_force_inline const CallstackFrameId& GetGhostFrame( const Int24& frame ) const { return m_data.ghostFrames[frame.Val()]; } + tracy_force_inline const GhostKey& GetGhostFrame( const Int24& frame ) const { return m_data.ghostFrames[frame.Val()]; } #endif tracy_force_inline const bool HasZoneExtra( const ZoneEvent& ev ) const { return ev.extra != 0; } @@ -735,6 +766,7 @@ private: bool UpdateSampleStatistics( uint32_t callstack, uint32_t count, bool canPostpone ); void UpdateSampleStatisticsPostponed( decltype(Worker::DataBlock::postponedSamples.begin())& it ); void UpdateSampleStatisticsImpl( const CallstackFrameData** frames, uint16_t framesCount, uint32_t count, const VarArray& cs ); + tracy_force_inline void GetStackWithInlines( Vector& ret, const VarArray& cs ); tracy_force_inline int AddGhostZone( const VarArray& cs, Vector* vec, uint64_t t ); #endif