diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj index 30da4b75..ad468652 100644 --- a/profiler/build/win32/Tracy.vcxproj +++ b/profiler/build/win32/Tracy.vcxproj @@ -143,6 +143,7 @@ + diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters index 4e57efaa..44a42acc 100644 --- a/profiler/build/win32/Tracy.vcxproj.filters +++ b/profiler/build/win32/Tracy.vcxproj.filters @@ -288,6 +288,9 @@ server + + server + diff --git a/server/TracyView.cpp b/server/TracyView.cpp index d5aeaf07..b938b617 100644 --- a/server/TracyView.cpp +++ b/server/TracyView.cpp @@ -6314,148 +6314,6 @@ void View::DrawSamplesStatistics( Vector& data, int64_t timeRange, Accu } } -void View::DrawMemoryAllocWindow() -{ - bool show = true; - ImGui::Begin( "Memory allocation", &show, ImGuiWindowFlags_AlwaysAutoResize ); - if( !ImGui::GetCurrentWindowRead()->SkipItems ) - { - const auto& mem = m_worker.GetMemoryNamed( m_memoryAllocInfoPool ); - const auto& ev = mem.data[m_memoryAllocInfoWindow]; - const auto tidAlloc = m_worker.DecompressThread( ev.ThreadAlloc() ); - const auto tidFree = m_worker.DecompressThread( ev.ThreadFree() ); - int idx = 0; - - if( ImGui::Button( ICON_FA_MICROSCOPE " Zoom to allocation" ) ) - { - ZoomToRange( ev.TimeAlloc(), ev.TimeFree() >= 0 ? ev.TimeFree() : m_worker.GetLastTime() ); - } - - if( m_worker.GetMemNameMap().size() > 1 ) - { - TextFocused( ICON_FA_ARCHIVE " Pool:", m_memoryAllocInfoPool == 0 ? "Default allocator" : m_worker.GetString( m_memoryAllocInfoPool ) ); - } - char buf[64]; - sprintf( buf, "0x%" PRIx64, ev.Ptr() ); - TextFocused( "Address:", buf ); - TextFocused( "Size:", MemSizeToString( ev.Size() ) ); - if( ev.Size() >= 10000ll ) - { - ImGui::SameLine(); - ImGui::TextDisabled( "(%s bytes)", RealToString( ev.Size() ) ); - } - ImGui::Separator(); - TextFocused( "Appeared at", TimeToStringExact( ev.TimeAlloc() ) ); - if( ImGui::IsItemClicked() ) CenterAtTime( ev.TimeAlloc() ); - ImGui::SameLine(); ImGui::Spacing(); ImGui::SameLine(); - SmallColorBox( GetThreadColor( tidAlloc, 0 ) ); - ImGui::SameLine(); - TextFocused( "Thread:", m_worker.GetThreadName( tidAlloc ) ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%s)", RealToString( tidAlloc ) ); - if( m_worker.IsThreadFiber( tidAlloc ) ) - { - ImGui::SameLine(); - TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" ); - } - if( ev.CsAlloc() != 0 ) - { - const auto cs = ev.CsAlloc(); - SmallCallstackButton( ICON_FA_ALIGN_JUSTIFY, cs, idx ); - ImGui::SameLine(); - DrawCallstackCalls( cs, 2 ); - } - if( ev.TimeFree() < 0 ) - { - TextDisabledUnformatted( "Allocation still active" ); - } - else - { - TextFocused( "Freed at", TimeToStringExact( ev.TimeFree() ) ); - if( ImGui::IsItemClicked() ) CenterAtTime( ev.TimeFree() ); - ImGui::SameLine(); ImGui::Spacing(); ImGui::SameLine(); - SmallColorBox( GetThreadColor( tidFree, 0 ) ); - ImGui::SameLine(); - TextFocused( "Thread:", m_worker.GetThreadName( tidFree ) ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%s)", RealToString( tidFree ) ); - if( m_worker.IsThreadFiber( tidFree ) ) - { - ImGui::SameLine(); - TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" ); - } - if( ev.csFree.Val() != 0 ) - { - const auto cs = ev.csFree.Val(); - SmallCallstackButton( ICON_FA_ALIGN_JUSTIFY, cs, idx ); - ImGui::SameLine(); - DrawCallstackCalls( cs, 2 ); - } - TextFocused( "Duration:", TimeToString( ev.TimeFree() - ev.TimeAlloc() ) ); - } - - bool sep = false; - auto zoneAlloc = FindZoneAtTime( tidAlloc, ev.TimeAlloc() ); - if( zoneAlloc ) - { - ImGui::Separator(); - sep = true; - const auto& srcloc = m_worker.GetSourceLocation( zoneAlloc->SrcLoc() ); - const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); - ImGui::PushID( idx++ ); - TextFocused( "Zone alloc:", txt ); - auto hover = ImGui::IsItemHovered(); - ImGui::PopID(); - if( ImGui::IsItemClicked() ) - { - ShowZoneInfo( *zoneAlloc ); - } - if( hover ) - { - m_zoneHighlight = zoneAlloc; - if( IsMouseClicked( 2 ) ) - { - ZoomToZone( *zoneAlloc ); - } - ZoneTooltip( *zoneAlloc ); - } - } - - if( ev.TimeFree() >= 0 ) - { - auto zoneFree = FindZoneAtTime( tidFree, ev.TimeFree() ); - if( zoneFree ) - { - if( !sep ) ImGui::Separator(); - const auto& srcloc = m_worker.GetSourceLocation( zoneFree->SrcLoc() ); - const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); - TextFocused( "Zone free:", txt ); - auto hover = ImGui::IsItemHovered(); - if( ImGui::IsItemClicked() ) - { - ShowZoneInfo( *zoneFree ); - } - if( hover ) - { - m_zoneHighlight = zoneFree; - if( IsMouseClicked( 2 ) ) - { - ZoomToZone( *zoneFree ); - } - ZoneTooltip( *zoneFree ); - } - if( zoneAlloc != 0 && zoneAlloc == zoneFree ) - { - ImGui::SameLine(); - TextDisabledUnformatted( "(same zone)" ); - } - } - } - } - ImGui::End(); - if( !show ) m_memoryAllocInfoWindow = -1; -} - void View::DrawTextEditor() { const auto scale = GetScale(); @@ -7461,254 +7319,6 @@ void View::DrawWaitStacks() ImGui::End(); } -void View::ListMemData( std::vector& vec, std::function DrawAddress, const char* id, int64_t startTime, uint64_t pool ) -{ - if( startTime == -1 ) startTime = 0; - if( ImGui::BeginTable( "##mem", 8, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY, ImVec2( 0, ImGui::GetTextLineHeightWithSpacing() * std::min( 1+vec.size(), 15 ) ) ) ) - { - ImGui::TableSetupScrollFreeze( 0, 1 ); - ImGui::TableSetupColumn( "Address", ImGuiTableColumnFlags_NoHide ); - ImGui::TableSetupColumn( "Size", ImGuiTableColumnFlags_PreferSortDescending ); - ImGui::TableSetupColumn( "Appeared at", ImGuiTableColumnFlags_DefaultSort ); - ImGui::TableSetupColumn( "Duration", ImGuiTableColumnFlags_PreferSortDescending ); - ImGui::TableSetupColumn( "Thread", ImGuiTableColumnFlags_NoSort ); - ImGui::TableSetupColumn( "Zone alloc", ImGuiTableColumnFlags_NoSort ); - ImGui::TableSetupColumn( "Zone free", ImGuiTableColumnFlags_NoSort ); - ImGui::TableSetupColumn( "Call stack", ImGuiTableColumnFlags_NoSort ); - ImGui::TableHeadersRow(); - - const auto& mem = m_worker.GetMemoryNamed( pool ); - const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs; - switch( sortspec.ColumnIndex ) - { - case 0: - if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) - { - pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return l->Ptr() < r->Ptr(); } ); - } - else - { - pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return l->Ptr() > r->Ptr(); } ); - } - break; - case 1: - if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) - { - pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return l->Size() < r->Size(); } ); - } - else - { - pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return l->Size() > r->Size(); } ); - } - break; - case 2: - if( sortspec.SortDirection == ImGuiSortDirection_Descending ) - { - std::reverse( vec.begin(), vec.end() ); - } - break; - case 3: - if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) - { - pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return ( l->TimeFree() - l->TimeAlloc() ) < ( r->TimeFree() - r->TimeAlloc() ); } ); - } - else - { - pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return ( l->TimeFree() - l->TimeAlloc() ) > ( r->TimeFree() - r->TimeAlloc() ); } ); - } - break; - default: - assert( false ); - break; - } - - int idx = 0; - ImGuiListClipper clipper; - clipper.Begin( vec.end() - vec.begin() ); - while( clipper.Step() ) - { - for( auto i=clipper.DisplayStart; iTimeAlloc(), v->TimeFree() >= 0 ? v->TimeFree() : m_worker.GetLastTime() ); - } - if( ImGui::IsItemHovered() ) - { - m_memoryAllocHover = arrIdx; - m_memoryAllocHoverWait = 2; - m_memoryAllocHoverPool = pool; - } - ImGui::TableNextColumn(); - ImGui::TextUnformatted( MemSizeToString( v->Size() ) ); - ImGui::TableNextColumn(); - ImGui::PushID( idx++ ); - if( ImGui::Selectable( TimeToStringExact( v->TimeAlloc() - startTime ) ) ) - { - CenterAtTime( v->TimeAlloc() ); - } - ImGui::PopID(); - ImGui::TableNextColumn(); - if( v->TimeFree() < 0 ) - { - TextColoredUnformatted( ImVec4( 0.6f, 1.f, 0.6f, 1.f ), TimeToString( m_worker.GetLastTime() - v->TimeAlloc() ) ); - ImGui::TableNextColumn(); - const auto tid = m_worker.DecompressThread( v->ThreadAlloc() ); - SmallColorBox( GetThreadColor( tid, 0 ) ); - ImGui::SameLine(); - ImGui::TextUnformatted( m_worker.GetThreadName( tid ) ); - } - else - { - ImGui::PushID( idx++ ); - if( ImGui::Selectable( TimeToString( v->TimeFree() - v->TimeAlloc() ) ) ) - { - CenterAtTime( v->TimeFree() ); - } - ImGui::PopID(); - ImGui::TableNextColumn(); - if( v->ThreadAlloc() == v->ThreadFree() ) - { - const auto tid = m_worker.DecompressThread( v->ThreadAlloc() ); - SmallColorBox( GetThreadColor( tid, 0 ) ); - ImGui::SameLine(); - ImGui::TextUnformatted( m_worker.GetThreadName( tid ) ); - } - else - { - const auto tidAlloc = m_worker.DecompressThread( v->ThreadAlloc() ); - const auto tidFree = m_worker.DecompressThread( v->ThreadFree() ); - SmallColorBox( GetThreadColor( tidAlloc, 0 ) ); - ImGui::SameLine(); - ImGui::TextUnformatted( m_worker.GetThreadName( tidAlloc ) ); - ImGui::SameLine(); - ImGui::TextUnformatted( "/" ); - ImGui::SameLine(); - SmallColorBox( GetThreadColor( tidFree, 0 ) ); - ImGui::SameLine(); - ImGui::TextUnformatted( m_worker.GetThreadName( tidFree ) ); - } - } - ImGui::TableNextColumn(); - auto zone = FindZoneAtTime( m_worker.DecompressThread( v->ThreadAlloc() ), v->TimeAlloc() ); - if( !zone ) - { - ImGui::TextUnformatted( "-" ); - } - else - { - const auto& srcloc = m_worker.GetSourceLocation( zone->SrcLoc() ); - const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); - ImGui::PushID( idx++ ); - auto sel = ImGui::Selectable( txt, m_zoneInfoWindow == zone ); - auto hover = ImGui::IsItemHovered(); - ImGui::PopID(); - if( sel ) - { - ShowZoneInfo( *zone ); - } - if( hover ) - { - m_zoneHighlight = zone; - if( IsMouseClicked( 2 ) ) - { - ZoomToZone( *zone ); - } - ZoneTooltip( *zone ); - } - } - ImGui::TableNextColumn(); - if( v->TimeFree() < 0 ) - { - TextColoredUnformatted( ImVec4( 0.6f, 1.f, 0.6f, 1.f ), "active" ); - } - else - { - auto zoneFree = FindZoneAtTime( m_worker.DecompressThread( v->ThreadFree() ), v->TimeFree() ); - if( !zoneFree ) - { - ImGui::TextUnformatted( "-" ); - } - else - { - const auto& srcloc = m_worker.GetSourceLocation( zoneFree->SrcLoc() ); - const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); - ImGui::PushID( idx++ ); - bool sel; - if( zoneFree == zone ) - { - ImGui::PushStyleColor( ImGuiCol_Text, ImVec4( 1.f, 1.f, 0.6f, 1.f ) ); - sel = ImGui::Selectable( txt, m_zoneInfoWindow == zoneFree ); - ImGui::PopStyleColor( 1 ); - } - else - { - sel = ImGui::Selectable( txt, m_zoneInfoWindow == zoneFree ); - } - auto hover = ImGui::IsItemHovered(); - ImGui::PopID(); - if( sel ) - { - ShowZoneInfo( *zoneFree ); - } - if( hover ) - { - m_zoneHighlight = zoneFree; - if( IsMouseClicked( 2 ) ) - { - ZoomToZone( *zoneFree ); - } - ZoneTooltip( *zoneFree ); - } - } - } - ImGui::TableNextColumn(); - if( v->CsAlloc() == 0 ) - { - TextDisabledUnformatted( "[alloc]" ); - } - else - { - SmallCallstackButton( "alloc", v->CsAlloc(), idx ); - } - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - if( v->csFree.Val() == 0 ) - { - TextDisabledUnformatted( "[free]" ); - } - else - { - SmallCallstackButton( "free", v->csFree.Val(), idx ); - } - } - } - ImGui::EndTable(); - } -} - template static tracy_force_inline T* GetFrameTreeItemNoGroup( unordered_flat_map& tree, CallstackFrameId idx, const Worker& worker ) { @@ -8107,507 +7717,6 @@ unordered_flat_map View::GetParentsCallstackFrameT return root; } - -enum { ChunkBits = 10 }; -enum { PageBits = 10 }; -enum { PageSize = 1 << PageBits }; -enum { PageChunkBits = ChunkBits + PageBits }; -enum { PageChunkSize = 1 << PageChunkBits }; - -uint32_t MemDecayColor[256] = { - 0x0, 0xFF077F07, 0xFF078007, 0xFF078207, 0xFF078307, 0xFF078507, 0xFF078707, 0xFF078807, - 0xFF078A07, 0xFF078B07, 0xFF078D07, 0xFF078F07, 0xFF079007, 0xFF089208, 0xFF089308, 0xFF089508, - 0xFF089708, 0xFF089808, 0xFF089A08, 0xFF089B08, 0xFF089D08, 0xFF089F08, 0xFF08A008, 0xFF08A208, - 0xFF09A309, 0xFF09A509, 0xFF09A709, 0xFF09A809, 0xFF09AA09, 0xFF09AB09, 0xFF09AD09, 0xFF09AF09, - 0xFF09B009, 0xFF09B209, 0xFF09B309, 0xFF09B509, 0xFF0AB70A, 0xFF0AB80A, 0xFF0ABA0A, 0xFF0ABB0A, - 0xFF0ABD0A, 0xFF0ABF0A, 0xFF0AC00A, 0xFF0AC20A, 0xFF0AC30A, 0xFF0AC50A, 0xFF0AC70A, 0xFF0BC80B, - 0xFF0BCA0B, 0xFF0BCB0B, 0xFF0BCD0B, 0xFF0BCF0B, 0xFF0BD00B, 0xFF0BD20B, 0xFF0BD30B, 0xFF0BD50B, - 0xFF0BD70B, 0xFF0BD80B, 0xFF0BDA0B, 0xFF0CDB0C, 0xFF0CDD0C, 0xFF0CDF0C, 0xFF0CE00C, 0xFF0CE20C, - 0xFF0CE30C, 0xFF0CE50C, 0xFF0CE70C, 0xFF0CE80C, 0xFF0CEA0C, 0xFF0CEB0C, 0xFF0DED0D, 0xFF0DEF0D, - 0xFF0DF00D, 0xFF0DF20D, 0xFF0DF30D, 0xFF0DF50D, 0xFF0DF70D, 0xFF0DF80D, 0xFF0DFA0D, 0xFF0DFB0D, - 0xFF0DFD0D, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, - 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, - 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, - 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, - 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, - 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF12FF12, - 0x0, 0xFF1212FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, - 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, - 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, - 0xFF1010FF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, - 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, - 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, - 0xFF0D0DFD, 0xFF0D0DFB, 0xFF0D0DFA, 0xFF0D0DF8, 0xFF0D0DF7, 0xFF0D0DF5, 0xFF0D0DF3, 0xFF0D0DF2, - 0xFF0D0DF0, 0xFF0D0DEF, 0xFF0D0DED, 0xFF0C0CEB, 0xFF0C0CEA, 0xFF0C0CE8, 0xFF0C0CE7, 0xFF0C0CE5, - 0xFF0C0CE3, 0xFF0C0CE2, 0xFF0C0CE0, 0xFF0C0CDF, 0xFF0C0CDD, 0xFF0C0CDB, 0xFF0B0BDA, 0xFF0B0BD8, - 0xFF0B0BD7, 0xFF0B0BD5, 0xFF0B0BD3, 0xFF0B0BD2, 0xFF0B0BD0, 0xFF0B0BCF, 0xFF0B0BCD, 0xFF0B0BCB, - 0xFF0B0BCA, 0xFF0B0BC8, 0xFF0A0AC7, 0xFF0A0AC5, 0xFF0A0AC3, 0xFF0A0AC2, 0xFF0A0AC0, 0xFF0A0ABF, - 0xFF0A0ABD, 0xFF0A0ABB, 0xFF0A0ABA, 0xFF0A0AB8, 0xFF0A0AB7, 0xFF0909B5, 0xFF0909B3, 0xFF0909B2, - 0xFF0909B0, 0xFF0909AF, 0xFF0909AD, 0xFF0909AB, 0xFF0909AA, 0xFF0909A8, 0xFF0909A7, 0xFF0909A5, - 0xFF0909A3, 0xFF0808A2, 0xFF0808A0, 0xFF08089F, 0xFF08089D, 0xFF08089B, 0xFF08089A, 0xFF080898, - 0xFF080897, 0xFF080895, 0xFF080893, 0xFF080892, 0xFF070790, 0xFF07078F, 0xFF07078D, 0xFF07078B, - 0xFF07078A, 0xFF070788, 0xFF070787, 0xFF070785, 0xFF070783, 0xFF070782, 0xFF070780, 0xFF07077F, -}; - -struct MemoryPage -{ - uint64_t page; - int8_t data[PageSize]; -}; - -static tracy_force_inline MemoryPage& GetPage( unordered_flat_map& memmap, uint64_t page ) -{ - auto it = memmap.find( page ); - if( it == memmap.end() ) - { - it = memmap.emplace( page, MemoryPage { page, {} } ).first; - } - return it->second; -} - -static tracy_force_inline void FillPages( unordered_flat_map& memmap, uint64_t c0, uint64_t c1, int8_t val ) -{ - auto p0 = c0 >> PageBits; - const auto p1 = c1 >> PageBits; - - if( p0 == p1 ) - { - const auto a0 = c0 & ( PageSize - 1 ); - const auto a1 = c1 & ( PageSize - 1 ); - - auto& page = GetPage( memmap, p0 ); - if( a0 == a1 ) - { - page.data[a0] = val; - } - else - { - memset( page.data + a0, val, a1 - a0 + 1 ); - } - } - else - { - { - const auto a0 = c0 & ( PageSize - 1 ); - auto& page = GetPage( memmap, p0 ); - memset( page.data + a0, val, PageSize - a0 ); - } - while( ++p0 < p1 ) - { - auto& page = GetPage( memmap, p0 ); - memset( page.data, val, PageSize ); - } - { - const auto a1 = c1 & ( PageSize - 1 ); - auto& page = GetPage( memmap, p1 ); - memset( page.data, val, a1 + 1 ); - } - } -} - -std::vector View::GetMemoryPages() const -{ - std::vector ret; - - static unordered_flat_map memmap; - - const auto& mem = m_worker.GetMemoryNamed( m_memInfo.pool ); - const auto memlow = mem.low; - - if( m_memInfo.range.active ) - { - auto it = std::lower_bound( mem.data.begin(), mem.data.end(), m_memInfo.range.min, []( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); - if( it != mem.data.end() ) - { - auto end = std::lower_bound( mem.data.begin(), mem.data.end(), m_memInfo.range.max, []( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); - while( it != end ) - { - auto& alloc = *it++; - - const auto a0 = alloc.Ptr() - memlow; - const auto a1 = a0 + alloc.Size(); - int8_t val = alloc.TimeFree() < 0 ? - int8_t( std::max( int64_t( 1 ), 127 - ( ( m_memInfo.range.max - alloc.TimeAlloc() ) >> 24 ) ) ) : - ( alloc.TimeFree() > m_memInfo.range.max ? - int8_t( std::max( int64_t( 1 ), 127 - ( ( m_memInfo.range.max - alloc.TimeAlloc() ) >> 24 ) ) ) : - int8_t( -std::max( int64_t( 1 ), 127 - ( ( m_memInfo.range.max - alloc.TimeFree() ) >> 24 ) ) ) ); - - const auto c0 = a0 >> ChunkBits; - const auto c1 = a1 >> ChunkBits; - - FillPages( memmap, c0, c1, val ); - } - } - } - else - { - const auto lastTime = m_worker.GetLastTime(); - for( auto& alloc : mem.data ) - { - const auto a0 = alloc.Ptr() - memlow; - const auto a1 = a0 + alloc.Size(); - const int8_t val = alloc.TimeFree() < 0 ? - int8_t( std::max( int64_t( 1 ), 127 - ( ( lastTime - std::min( lastTime, alloc.TimeAlloc() ) ) >> 24 ) ) ) : - int8_t( -std::max( int64_t( 1 ), 127 - ( ( lastTime - std::min( lastTime, alloc.TimeFree() ) ) >> 24 ) ) ); - - const auto c0 = a0 >> ChunkBits; - const auto c1 = a1 >> ChunkBits; - - FillPages( memmap, c0, c1, val ); - } - } - - std::vector::const_iterator> itmap; - itmap.reserve( memmap.size() ); - ret.reserve( memmap.size() ); - for( auto it = memmap.begin(); it != memmap.end(); ++it ) itmap.emplace_back( it ); - pdqsort_branchless( itmap.begin(), itmap.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second.page < rhs->second.page; } ); - for( auto& v : itmap ) ret.emplace_back( v->second ); - - memmap.clear(); - return ret; -} - -void View::DrawMemory() -{ - const auto scale = GetScale(); - ImGui::SetNextWindowSize( ImVec2( 1100 * scale, 500 * scale ), ImGuiCond_FirstUseEver ); - ImGui::Begin( "Memory", &m_memInfo.show, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse ); - if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; } - - auto& memNameMap = m_worker.GetMemNameMap(); - if( memNameMap.size() > 1 ) - { - TextDisabledUnformatted( ICON_FA_ARCHIVE " Memory pool:" ); - ImGui::SameLine(); - if( ImGui::BeginCombo( "##memoryPool", m_memInfo.pool == 0 ? "Default allocator" : m_worker.GetString( m_memInfo.pool ) ) ) - { - for( auto& v : memNameMap ) - { - if( ImGui::Selectable( v.first == 0 ? "Default allocator" : m_worker.GetString( v.first ) ) ) - { - m_memInfo.pool = v.first; - m_memInfo.showAllocList = false; - } - } - ImGui::EndCombo(); - } - ImGui::Separator(); - } - - auto& mem = m_worker.GetMemoryNamed( m_memInfo.pool ); - if( mem.data.empty() ) - { - ImGui::TextWrapped( "No memory data collected." ); - ImGui::End(); - return; - } - - TextDisabledUnformatted( "Total allocations:" ); - ImGui::SameLine(); - ImGui::Text( "%-15s", RealToString( mem.data.size() ) ); - ImGui::SameLine(); - TextDisabledUnformatted( "Active allocations:" ); - ImGui::SameLine(); - ImGui::Text( "%-15s", RealToString( mem.active.size() ) ); - ImGui::SameLine(); - TextDisabledUnformatted( "Memory usage:" ); - ImGui::SameLine(); - ImGui::Text( "%-15s", MemSizeToString( mem.usage ) ); - ImGui::SameLine(); - TextFocused( "Memory span:", MemSizeToString( mem.high - mem.low ) ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - DrawHelpMarker( - "Click on address to display memory allocation info window. Middle click to zoom to allocation range.\n" - "Active allocations are displayed using green color.\n" - "A single thread is displayed if alloc and free was performed on the same thread. Otherwise two threads are displayed in order: alloc, free.\n" - "If alloc and free is performed in the same zone, the free zone is displayed in yellow color." ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - ImGui::SeparatorEx( ImGuiSeparatorFlags_Vertical ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 2, 2 ) ); - if( ImGui::Checkbox( "Limit range", &m_memInfo.range.active ) ) - { - if( m_memInfo.range.active && m_memInfo.range.min == 0 && m_memInfo.range.max == 0 ) - { - m_memInfo.range.min = m_vd.zvStart; - m_memInfo.range.max = m_vd.zvEnd; - } - } - if( m_memInfo.range.active ) - { - ImGui::SameLine(); - TextColoredUnformatted( 0xFF00FFFF, ICON_FA_EXCLAMATION_TRIANGLE ); - ImGui::SameLine(); - ToggleButton( ICON_FA_RULER " Limits", m_showRanges ); - } - ImGui::PopStyleVar(); - - ImGui::Separator(); - ImGui::BeginChild( "##memory" ); - if( ImGui::TreeNode( ICON_FA_AT " Allocations" ) ) - { - bool findClicked = ImGui::InputTextWithHint( "###address", "Enter memory address to search for", m_memInfo.pattern, 1024, ImGuiInputTextFlags_EnterReturnsTrue ); - ImGui::SameLine(); - findClicked |= ImGui::Button( ICON_FA_SEARCH " Find" ); - if( findClicked ) - { - m_memInfo.ptrFind = strtoull( m_memInfo.pattern, nullptr, 0 ); - } - ImGui::SameLine(); - if( ImGui::Button( ICON_FA_BACKSPACE " Clear" ) ) - { - m_memInfo.ptrFind = 0; - m_memInfo.pattern[0] = '\0'; - } - - if( m_memInfo.ptrFind != 0 ) - { - std::vector match; - match.reserve( mem.active.size() ); // heuristic - if( m_memInfo.range.active ) - { - auto it = std::lower_bound( mem.data.begin(), mem.data.end(), m_memInfo.range.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); - if( it != mem.data.end() ) - { - auto end = std::lower_bound( it, mem.data.end(), m_memInfo.range.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); - while( it != end ) - { - if( it->Ptr() <= m_memInfo.ptrFind && it->Ptr() + it->Size() > m_memInfo.ptrFind ) - { - match.emplace_back( it ); - } - ++it; - } - } - } - else - { - for( auto& v : mem.data ) - { - if( v.Ptr() <= m_memInfo.ptrFind && v.Ptr() + v.Size() > m_memInfo.ptrFind ) - { - match.emplace_back( &v ); - } - } - } - - if( match.empty() ) - { - ImGui::TextUnformatted( "Found no allocations at given address" ); - } - else - { - ListMemData( match, [this]( auto v ) { - if( v->Ptr() == m_memInfo.ptrFind ) - { - ImGui::Text( "0x%" PRIx64, m_memInfo.ptrFind ); - } - else - { - ImGui::Text( "0x%" PRIx64 "+%" PRIu64, v->Ptr(), m_memInfo.ptrFind - v->Ptr() ); - } - }, "##allocations", -1, m_memInfo.pool ); - } - } - ImGui::TreePop(); - } - - ImGui::Separator(); - if( ImGui::TreeNode( ICON_FA_HEARTBEAT " Active allocations" ) ) - { - uint64_t total = 0; - std::vector items; - items.reserve( mem.active.size() ); - if( m_memInfo.range.active ) - { - auto it = std::lower_bound( mem.data.begin(), mem.data.end(), m_memInfo.range.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); - if( it != mem.data.end() ) - { - auto end = std::lower_bound( it, mem.data.end(), m_memInfo.range.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); - while( it != end ) - { - const auto tf = it->TimeFree(); - if( tf < 0 || tf >= m_memInfo.range.max ) - { - items.emplace_back( it ); - total += it->Size(); - } - ++it; - } - } - } - else - { - auto ptr = mem.data.data(); - for( auto& v : mem.active ) items.emplace_back( ptr + v.second ); - pdqsort_branchless( items.begin(), items.end(), []( const auto& lhs, const auto& rhs ) { return lhs->TimeAlloc() < rhs->TimeAlloc(); } ); - total = mem.usage; - } - - ImGui::SameLine(); - ImGui::TextDisabled( "(%s)", RealToString( items.size() ) ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - TextFocused( "Memory usage:", MemSizeToString( total ) ); - - if( !items.empty() ) - { - ListMemData( items, []( auto v ) { - ImGui::Text( "0x%" PRIx64, v->Ptr() ); - }, "##activeMem", -1, m_memInfo.pool ); - } - else - { - TextDisabledUnformatted( "No active allocations" ); - } - ImGui::TreePop(); - } - - ImGui::Separator(); - if( ImGui::TreeNode( ICON_FA_MAP " Memory map" ) ) - { - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - TextFocused( "Single pixel:", MemSizeToString( 1 << ChunkBits ) ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - TextFocused( "Single line:", MemSizeToString( PageChunkSize ) ); - - auto pages = GetMemoryPages(); - const size_t lines = pages.size(); - - ImGui::BeginChild( "##memMap", ImVec2( PageSize + 2, lines + 2 ), false ); - auto draw = ImGui::GetWindowDrawList(); - const auto wpos = ImGui::GetCursorScreenPos() + ImVec2( 1, 1 ); - const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); - draw->AddRect( wpos - ImVec2( 1, 1 ), wpos + ImVec2( PageSize + 1, lines + 1 ), 0xFF666666 ); - draw->AddRectFilled( wpos, wpos + ImVec2( PageSize, lines ), 0xFF444444 ); - - size_t line = 0; - for( auto& page : pages ) - { - size_t idx = 0; - while( idx < PageSize ) - { - if( page.data[idx] == 0 ) - { - do - { - idx++; - } - while( idx < PageSize && page.data[idx] == 0 ); - } - else - { - auto val = page.data[idx]; - const auto i0 = idx; - do - { - idx++; - } - while( idx < PageSize && page.data[idx] == val ); - DrawLine( draw, dpos + ImVec2( i0, line ), dpos + ImVec2( idx, line ), MemDecayColor[(uint8_t)val] ); - } - } - line++; - } - - ImGui::EndChild(); - ImGui::TreePop(); - } - - ImGui::PushID( m_memInfo.pool ); - ImGui::Separator(); - if( ImGui::TreeNode( ICON_FA_TREE " Bottom-up call stack tree" ) ) - { - ImGui::SameLine(); - DrawHelpMarker( "Press ctrl key to display allocation info tooltip. Right click on function name to display allocations list." ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - SmallCheckbox( "Group by function name", &m_groupCallstackTreeByNameBottomUp ); - ImGui::SameLine(); - DrawHelpMarker( "If enabled, only one source location will be displayed (which may be incorrect)." ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - bool activeOnlyBottomUp = m_memRangeBottomUp == MemRange::Active; - if( SmallCheckbox( "Only active allocations", &activeOnlyBottomUp ) ) - m_memRangeBottomUp = activeOnlyBottomUp ? MemRange::Active : MemRange::Full; - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - bool inactiveOnlyBottomUp = m_memRangeBottomUp == MemRange::Inactive; - if( SmallCheckbox( "Only inactive allocations", &inactiveOnlyBottomUp ) ) - m_memRangeBottomUp = inactiveOnlyBottomUp ? MemRange::Inactive : MemRange::Full; - - auto tree = GetCallstackFrameTreeBottomUp( mem ); - if( !tree.empty() ) - { - int idx = 0; - DrawFrameTreeLevel( tree, idx ); - } - else - { - TextDisabledUnformatted( "No call stack data collected" ); - } - - ImGui::TreePop(); - } - - ImGui::Separator(); - if( ImGui::TreeNode( ICON_FA_TREE " Top-down call stack tree" ) ) - { - ImGui::SameLine(); - DrawHelpMarker( "Press ctrl key to display allocation info tooltip. Right click on function name to display allocations list." ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - SmallCheckbox( "Group by function name", &m_groupCallstackTreeByNameTopDown ); - ImGui::SameLine(); - DrawHelpMarker( "If enabled, only one source location will be displayed (which may be incorrect)." ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - bool activeOnlyTopDown = m_memRangeTopDown == MemRange::Active; - if( SmallCheckbox( "Only active allocations", &activeOnlyTopDown ) ) - m_memRangeTopDown = activeOnlyTopDown ? MemRange::Active : MemRange::Full; - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - bool inactiveOnlyTopDown = m_memRangeTopDown == MemRange::Inactive; - if( SmallCheckbox( "Only inactive allocations", &inactiveOnlyTopDown ) ) - m_memRangeTopDown = inactiveOnlyTopDown ? MemRange::Inactive : MemRange::Full; - - auto tree = GetCallstackFrameTreeTopDown( mem ); - if( !tree.empty() ) - { - int idx = 0; - DrawFrameTreeLevel( tree, idx ); - } - else - { - TextDisabledUnformatted( "No call stack data collected" ); - } - - ImGui::TreePop(); - } - ImGui::PopID(); - - ImGui::EndChild(); - ImGui::End(); -} - void View::DrawFrameTreeLevel( const unordered_flat_map& tree, int& idx ) { auto& io = ImGui::GetIO(); diff --git a/server/TracyView_Memory.cpp b/server/TracyView_Memory.cpp new file mode 100644 index 00000000..91982231 --- /dev/null +++ b/server/TracyView_Memory.cpp @@ -0,0 +1,900 @@ +#include + +#include "TracyMouse.hpp" +#include "TracyPrint.hpp" +#include "TracyView.hpp" + +namespace tracy +{ + +enum { ChunkBits = 10 }; +enum { PageBits = 10 }; +enum { PageSize = 1 << PageBits }; +enum { PageChunkBits = ChunkBits + PageBits }; +enum { PageChunkSize = 1 << PageChunkBits }; + +uint32_t MemDecayColor[256] = { + 0x0, 0xFF077F07, 0xFF078007, 0xFF078207, 0xFF078307, 0xFF078507, 0xFF078707, 0xFF078807, + 0xFF078A07, 0xFF078B07, 0xFF078D07, 0xFF078F07, 0xFF079007, 0xFF089208, 0xFF089308, 0xFF089508, + 0xFF089708, 0xFF089808, 0xFF089A08, 0xFF089B08, 0xFF089D08, 0xFF089F08, 0xFF08A008, 0xFF08A208, + 0xFF09A309, 0xFF09A509, 0xFF09A709, 0xFF09A809, 0xFF09AA09, 0xFF09AB09, 0xFF09AD09, 0xFF09AF09, + 0xFF09B009, 0xFF09B209, 0xFF09B309, 0xFF09B509, 0xFF0AB70A, 0xFF0AB80A, 0xFF0ABA0A, 0xFF0ABB0A, + 0xFF0ABD0A, 0xFF0ABF0A, 0xFF0AC00A, 0xFF0AC20A, 0xFF0AC30A, 0xFF0AC50A, 0xFF0AC70A, 0xFF0BC80B, + 0xFF0BCA0B, 0xFF0BCB0B, 0xFF0BCD0B, 0xFF0BCF0B, 0xFF0BD00B, 0xFF0BD20B, 0xFF0BD30B, 0xFF0BD50B, + 0xFF0BD70B, 0xFF0BD80B, 0xFF0BDA0B, 0xFF0CDB0C, 0xFF0CDD0C, 0xFF0CDF0C, 0xFF0CE00C, 0xFF0CE20C, + 0xFF0CE30C, 0xFF0CE50C, 0xFF0CE70C, 0xFF0CE80C, 0xFF0CEA0C, 0xFF0CEB0C, 0xFF0DED0D, 0xFF0DEF0D, + 0xFF0DF00D, 0xFF0DF20D, 0xFF0DF30D, 0xFF0DF50D, 0xFF0DF70D, 0xFF0DF80D, 0xFF0DFA0D, 0xFF0DFB0D, + 0xFF0DFD0D, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, + 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0EFF0E, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, + 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, 0xFF0FFF0F, + 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, + 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF10FF10, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, + 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF11FF11, 0xFF12FF12, + 0x0, 0xFF1212FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, + 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1111FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, + 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, 0xFF1010FF, + 0xFF1010FF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, + 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0F0FFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, + 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, 0xFF0E0EFF, + 0xFF0D0DFD, 0xFF0D0DFB, 0xFF0D0DFA, 0xFF0D0DF8, 0xFF0D0DF7, 0xFF0D0DF5, 0xFF0D0DF3, 0xFF0D0DF2, + 0xFF0D0DF0, 0xFF0D0DEF, 0xFF0D0DED, 0xFF0C0CEB, 0xFF0C0CEA, 0xFF0C0CE8, 0xFF0C0CE7, 0xFF0C0CE5, + 0xFF0C0CE3, 0xFF0C0CE2, 0xFF0C0CE0, 0xFF0C0CDF, 0xFF0C0CDD, 0xFF0C0CDB, 0xFF0B0BDA, 0xFF0B0BD8, + 0xFF0B0BD7, 0xFF0B0BD5, 0xFF0B0BD3, 0xFF0B0BD2, 0xFF0B0BD0, 0xFF0B0BCF, 0xFF0B0BCD, 0xFF0B0BCB, + 0xFF0B0BCA, 0xFF0B0BC8, 0xFF0A0AC7, 0xFF0A0AC5, 0xFF0A0AC3, 0xFF0A0AC2, 0xFF0A0AC0, 0xFF0A0ABF, + 0xFF0A0ABD, 0xFF0A0ABB, 0xFF0A0ABA, 0xFF0A0AB8, 0xFF0A0AB7, 0xFF0909B5, 0xFF0909B3, 0xFF0909B2, + 0xFF0909B0, 0xFF0909AF, 0xFF0909AD, 0xFF0909AB, 0xFF0909AA, 0xFF0909A8, 0xFF0909A7, 0xFF0909A5, + 0xFF0909A3, 0xFF0808A2, 0xFF0808A0, 0xFF08089F, 0xFF08089D, 0xFF08089B, 0xFF08089A, 0xFF080898, + 0xFF080897, 0xFF080895, 0xFF080893, 0xFF080892, 0xFF070790, 0xFF07078F, 0xFF07078D, 0xFF07078B, + 0xFF07078A, 0xFF070788, 0xFF070787, 0xFF070785, 0xFF070783, 0xFF070782, 0xFF070780, 0xFF07077F, +}; + +struct MemoryPage +{ + uint64_t page; + int8_t data[PageSize]; +}; + +static tracy_force_inline MemoryPage& GetPage( unordered_flat_map& memmap, uint64_t page ) +{ + auto it = memmap.find( page ); + if( it == memmap.end() ) + { + it = memmap.emplace( page, MemoryPage { page, {} } ).first; + } + return it->second; +} + +static tracy_force_inline void FillPages( unordered_flat_map& memmap, uint64_t c0, uint64_t c1, int8_t val ) +{ + auto p0 = c0 >> PageBits; + const auto p1 = c1 >> PageBits; + + if( p0 == p1 ) + { + const auto a0 = c0 & ( PageSize - 1 ); + const auto a1 = c1 & ( PageSize - 1 ); + + auto& page = GetPage( memmap, p0 ); + if( a0 == a1 ) + { + page.data[a0] = val; + } + else + { + memset( page.data + a0, val, a1 - a0 + 1 ); + } + } + else + { + { + const auto a0 = c0 & ( PageSize - 1 ); + auto& page = GetPage( memmap, p0 ); + memset( page.data + a0, val, PageSize - a0 ); + } + while( ++p0 < p1 ) + { + auto& page = GetPage( memmap, p0 ); + memset( page.data, val, PageSize ); + } + { + const auto a1 = c1 & ( PageSize - 1 ); + auto& page = GetPage( memmap, p1 ); + memset( page.data, val, a1 + 1 ); + } + } +} + +std::vector View::GetMemoryPages() const +{ + std::vector ret; + + static unordered_flat_map memmap; + + const auto& mem = m_worker.GetMemoryNamed( m_memInfo.pool ); + const auto memlow = mem.low; + + if( m_memInfo.range.active ) + { + auto it = std::lower_bound( mem.data.begin(), mem.data.end(), m_memInfo.range.min, []( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); + if( it != mem.data.end() ) + { + auto end = std::lower_bound( mem.data.begin(), mem.data.end(), m_memInfo.range.max, []( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); + while( it != end ) + { + auto& alloc = *it++; + + const auto a0 = alloc.Ptr() - memlow; + const auto a1 = a0 + alloc.Size(); + int8_t val = alloc.TimeFree() < 0 ? + int8_t( std::max( int64_t( 1 ), 127 - ( ( m_memInfo.range.max - alloc.TimeAlloc() ) >> 24 ) ) ) : + ( alloc.TimeFree() > m_memInfo.range.max ? + int8_t( std::max( int64_t( 1 ), 127 - ( ( m_memInfo.range.max - alloc.TimeAlloc() ) >> 24 ) ) ) : + int8_t( -std::max( int64_t( 1 ), 127 - ( ( m_memInfo.range.max - alloc.TimeFree() ) >> 24 ) ) ) ); + + const auto c0 = a0 >> ChunkBits; + const auto c1 = a1 >> ChunkBits; + + FillPages( memmap, c0, c1, val ); + } + } + } + else + { + const auto lastTime = m_worker.GetLastTime(); + for( auto& alloc : mem.data ) + { + const auto a0 = alloc.Ptr() - memlow; + const auto a1 = a0 + alloc.Size(); + const int8_t val = alloc.TimeFree() < 0 ? + int8_t( std::max( int64_t( 1 ), 127 - ( ( lastTime - std::min( lastTime, alloc.TimeAlloc() ) ) >> 24 ) ) ) : + int8_t( -std::max( int64_t( 1 ), 127 - ( ( lastTime - std::min( lastTime, alloc.TimeFree() ) ) >> 24 ) ) ); + + const auto c0 = a0 >> ChunkBits; + const auto c1 = a1 >> ChunkBits; + + FillPages( memmap, c0, c1, val ); + } + } + + std::vector::const_iterator> itmap; + itmap.reserve( memmap.size() ); + ret.reserve( memmap.size() ); + for( auto it = memmap.begin(); it != memmap.end(); ++it ) itmap.emplace_back( it ); + pdqsort_branchless( itmap.begin(), itmap.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second.page < rhs->second.page; } ); + for( auto& v : itmap ) ret.emplace_back( v->second ); + + memmap.clear(); + return ret; +} + +void View::DrawMemory() +{ + const auto scale = GetScale(); + ImGui::SetNextWindowSize( ImVec2( 1100 * scale, 500 * scale ), ImGuiCond_FirstUseEver ); + ImGui::Begin( "Memory", &m_memInfo.show, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse ); + if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; } + + auto& memNameMap = m_worker.GetMemNameMap(); + if( memNameMap.size() > 1 ) + { + TextDisabledUnformatted( ICON_FA_ARCHIVE " Memory pool:" ); + ImGui::SameLine(); + if( ImGui::BeginCombo( "##memoryPool", m_memInfo.pool == 0 ? "Default allocator" : m_worker.GetString( m_memInfo.pool ) ) ) + { + for( auto& v : memNameMap ) + { + if( ImGui::Selectable( v.first == 0 ? "Default allocator" : m_worker.GetString( v.first ) ) ) + { + m_memInfo.pool = v.first; + m_memInfo.showAllocList = false; + } + } + ImGui::EndCombo(); + } + ImGui::Separator(); + } + + auto& mem = m_worker.GetMemoryNamed( m_memInfo.pool ); + if( mem.data.empty() ) + { + ImGui::TextWrapped( "No memory data collected." ); + ImGui::End(); + return; + } + + TextDisabledUnformatted( "Total allocations:" ); + ImGui::SameLine(); + ImGui::Text( "%-15s", RealToString( mem.data.size() ) ); + ImGui::SameLine(); + TextDisabledUnformatted( "Active allocations:" ); + ImGui::SameLine(); + ImGui::Text( "%-15s", RealToString( mem.active.size() ) ); + ImGui::SameLine(); + TextDisabledUnformatted( "Memory usage:" ); + ImGui::SameLine(); + ImGui::Text( "%-15s", MemSizeToString( mem.usage ) ); + ImGui::SameLine(); + TextFocused( "Memory span:", MemSizeToString( mem.high - mem.low ) ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + DrawHelpMarker( + "Click on address to display memory allocation info window. Middle click to zoom to allocation range.\n" + "Active allocations are displayed using green color.\n" + "A single thread is displayed if alloc and free was performed on the same thread. Otherwise two threads are displayed in order: alloc, free.\n" + "If alloc and free is performed in the same zone, the free zone is displayed in yellow color." ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + ImGui::SeparatorEx( ImGuiSeparatorFlags_Vertical ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 2, 2 ) ); + if( ImGui::Checkbox( "Limit range", &m_memInfo.range.active ) ) + { + if( m_memInfo.range.active && m_memInfo.range.min == 0 && m_memInfo.range.max == 0 ) + { + m_memInfo.range.min = m_vd.zvStart; + m_memInfo.range.max = m_vd.zvEnd; + } + } + if( m_memInfo.range.active ) + { + ImGui::SameLine(); + TextColoredUnformatted( 0xFF00FFFF, ICON_FA_EXCLAMATION_TRIANGLE ); + ImGui::SameLine(); + ToggleButton( ICON_FA_RULER " Limits", m_showRanges ); + } + ImGui::PopStyleVar(); + + ImGui::Separator(); + ImGui::BeginChild( "##memory" ); + if( ImGui::TreeNode( ICON_FA_AT " Allocations" ) ) + { + bool findClicked = ImGui::InputTextWithHint( "###address", "Enter memory address to search for", m_memInfo.pattern, 1024, ImGuiInputTextFlags_EnterReturnsTrue ); + ImGui::SameLine(); + findClicked |= ImGui::Button( ICON_FA_SEARCH " Find" ); + if( findClicked ) + { + m_memInfo.ptrFind = strtoull( m_memInfo.pattern, nullptr, 0 ); + } + ImGui::SameLine(); + if( ImGui::Button( ICON_FA_BACKSPACE " Clear" ) ) + { + m_memInfo.ptrFind = 0; + m_memInfo.pattern[0] = '\0'; + } + + if( m_memInfo.ptrFind != 0 ) + { + std::vector match; + match.reserve( mem.active.size() ); // heuristic + if( m_memInfo.range.active ) + { + auto it = std::lower_bound( mem.data.begin(), mem.data.end(), m_memInfo.range.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); + if( it != mem.data.end() ) + { + auto end = std::lower_bound( it, mem.data.end(), m_memInfo.range.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); + while( it != end ) + { + if( it->Ptr() <= m_memInfo.ptrFind && it->Ptr() + it->Size() > m_memInfo.ptrFind ) + { + match.emplace_back( it ); + } + ++it; + } + } + } + else + { + for( auto& v : mem.data ) + { + if( v.Ptr() <= m_memInfo.ptrFind && v.Ptr() + v.Size() > m_memInfo.ptrFind ) + { + match.emplace_back( &v ); + } + } + } + + if( match.empty() ) + { + ImGui::TextUnformatted( "Found no allocations at given address" ); + } + else + { + ListMemData( match, [this]( auto v ) { + if( v->Ptr() == m_memInfo.ptrFind ) + { + ImGui::Text( "0x%" PRIx64, m_memInfo.ptrFind ); + } + else + { + ImGui::Text( "0x%" PRIx64 "+%" PRIu64, v->Ptr(), m_memInfo.ptrFind - v->Ptr() ); + } + }, "##allocations", -1, m_memInfo.pool ); + } + } + ImGui::TreePop(); + } + + ImGui::Separator(); + if( ImGui::TreeNode( ICON_FA_HEARTBEAT " Active allocations" ) ) + { + uint64_t total = 0; + std::vector items; + items.reserve( mem.active.size() ); + if( m_memInfo.range.active ) + { + auto it = std::lower_bound( mem.data.begin(), mem.data.end(), m_memInfo.range.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); + if( it != mem.data.end() ) + { + auto end = std::lower_bound( it, mem.data.end(), m_memInfo.range.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } ); + while( it != end ) + { + const auto tf = it->TimeFree(); + if( tf < 0 || tf >= m_memInfo.range.max ) + { + items.emplace_back( it ); + total += it->Size(); + } + ++it; + } + } + } + else + { + auto ptr = mem.data.data(); + for( auto& v : mem.active ) items.emplace_back( ptr + v.second ); + pdqsort_branchless( items.begin(), items.end(), []( const auto& lhs, const auto& rhs ) { return lhs->TimeAlloc() < rhs->TimeAlloc(); } ); + total = mem.usage; + } + + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( items.size() ) ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + TextFocused( "Memory usage:", MemSizeToString( total ) ); + + if( !items.empty() ) + { + ListMemData( items, []( auto v ) { + ImGui::Text( "0x%" PRIx64, v->Ptr() ); + }, "##activeMem", -1, m_memInfo.pool ); + } + else + { + TextDisabledUnformatted( "No active allocations" ); + } + ImGui::TreePop(); + } + + ImGui::Separator(); + if( ImGui::TreeNode( ICON_FA_MAP " Memory map" ) ) + { + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + TextFocused( "Single pixel:", MemSizeToString( 1 << ChunkBits ) ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + TextFocused( "Single line:", MemSizeToString( PageChunkSize ) ); + + auto pages = GetMemoryPages(); + const size_t lines = pages.size(); + + ImGui::BeginChild( "##memMap", ImVec2( PageSize + 2, lines + 2 ), false ); + auto draw = ImGui::GetWindowDrawList(); + const auto wpos = ImGui::GetCursorScreenPos() + ImVec2( 1, 1 ); + const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); + draw->AddRect( wpos - ImVec2( 1, 1 ), wpos + ImVec2( PageSize + 1, lines + 1 ), 0xFF666666 ); + draw->AddRectFilled( wpos, wpos + ImVec2( PageSize, lines ), 0xFF444444 ); + + size_t line = 0; + for( auto& page : pages ) + { + size_t idx = 0; + while( idx < PageSize ) + { + if( page.data[idx] == 0 ) + { + do + { + idx++; + } + while( idx < PageSize && page.data[idx] == 0 ); + } + else + { + auto val = page.data[idx]; + const auto i0 = idx; + do + { + idx++; + } + while( idx < PageSize && page.data[idx] == val ); + DrawLine( draw, dpos + ImVec2( i0, line ), dpos + ImVec2( idx, line ), MemDecayColor[(uint8_t)val] ); + } + } + line++; + } + + ImGui::EndChild(); + ImGui::TreePop(); + } + + ImGui::PushID( m_memInfo.pool ); + ImGui::Separator(); + if( ImGui::TreeNode( ICON_FA_TREE " Bottom-up call stack tree" ) ) + { + ImGui::SameLine(); + DrawHelpMarker( "Press ctrl key to display allocation info tooltip. Right click on function name to display allocations list." ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + SmallCheckbox( "Group by function name", &m_groupCallstackTreeByNameBottomUp ); + ImGui::SameLine(); + DrawHelpMarker( "If enabled, only one source location will be displayed (which may be incorrect)." ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + bool activeOnlyBottomUp = m_memRangeBottomUp == MemRange::Active; + if( SmallCheckbox( "Only active allocations", &activeOnlyBottomUp ) ) + m_memRangeBottomUp = activeOnlyBottomUp ? MemRange::Active : MemRange::Full; + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + bool inactiveOnlyBottomUp = m_memRangeBottomUp == MemRange::Inactive; + if( SmallCheckbox( "Only inactive allocations", &inactiveOnlyBottomUp ) ) + m_memRangeBottomUp = inactiveOnlyBottomUp ? MemRange::Inactive : MemRange::Full; + + auto tree = GetCallstackFrameTreeBottomUp( mem ); + if( !tree.empty() ) + { + int idx = 0; + DrawFrameTreeLevel( tree, idx ); + } + else + { + TextDisabledUnformatted( "No call stack data collected" ); + } + + ImGui::TreePop(); + } + + ImGui::Separator(); + if( ImGui::TreeNode( ICON_FA_TREE " Top-down call stack tree" ) ) + { + ImGui::SameLine(); + DrawHelpMarker( "Press ctrl key to display allocation info tooltip. Right click on function name to display allocations list." ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + SmallCheckbox( "Group by function name", &m_groupCallstackTreeByNameTopDown ); + ImGui::SameLine(); + DrawHelpMarker( "If enabled, only one source location will be displayed (which may be incorrect)." ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + bool activeOnlyTopDown = m_memRangeTopDown == MemRange::Active; + if( SmallCheckbox( "Only active allocations", &activeOnlyTopDown ) ) + m_memRangeTopDown = activeOnlyTopDown ? MemRange::Active : MemRange::Full; + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + bool inactiveOnlyTopDown = m_memRangeTopDown == MemRange::Inactive; + if( SmallCheckbox( "Only inactive allocations", &inactiveOnlyTopDown ) ) + m_memRangeTopDown = inactiveOnlyTopDown ? MemRange::Inactive : MemRange::Full; + + auto tree = GetCallstackFrameTreeTopDown( mem ); + if( !tree.empty() ) + { + int idx = 0; + DrawFrameTreeLevel( tree, idx ); + } + else + { + TextDisabledUnformatted( "No call stack data collected" ); + } + + ImGui::TreePop(); + } + ImGui::PopID(); + + ImGui::EndChild(); + ImGui::End(); +} + +void View::DrawMemoryAllocWindow() +{ + bool show = true; + ImGui::Begin( "Memory allocation", &show, ImGuiWindowFlags_AlwaysAutoResize ); + if( !ImGui::GetCurrentWindowRead()->SkipItems ) + { + const auto& mem = m_worker.GetMemoryNamed( m_memoryAllocInfoPool ); + const auto& ev = mem.data[m_memoryAllocInfoWindow]; + const auto tidAlloc = m_worker.DecompressThread( ev.ThreadAlloc() ); + const auto tidFree = m_worker.DecompressThread( ev.ThreadFree() ); + int idx = 0; + + if( ImGui::Button( ICON_FA_MICROSCOPE " Zoom to allocation" ) ) + { + ZoomToRange( ev.TimeAlloc(), ev.TimeFree() >= 0 ? ev.TimeFree() : m_worker.GetLastTime() ); + } + + if( m_worker.GetMemNameMap().size() > 1 ) + { + TextFocused( ICON_FA_ARCHIVE " Pool:", m_memoryAllocInfoPool == 0 ? "Default allocator" : m_worker.GetString( m_memoryAllocInfoPool ) ); + } + char buf[64]; + sprintf( buf, "0x%" PRIx64, ev.Ptr() ); + TextFocused( "Address:", buf ); + TextFocused( "Size:", MemSizeToString( ev.Size() ) ); + if( ev.Size() >= 10000ll ) + { + ImGui::SameLine(); + ImGui::TextDisabled( "(%s bytes)", RealToString( ev.Size() ) ); + } + ImGui::Separator(); + TextFocused( "Appeared at", TimeToStringExact( ev.TimeAlloc() ) ); + if( ImGui::IsItemClicked() ) CenterAtTime( ev.TimeAlloc() ); + ImGui::SameLine(); ImGui::Spacing(); ImGui::SameLine(); + SmallColorBox( GetThreadColor( tidAlloc, 0 ) ); + ImGui::SameLine(); + TextFocused( "Thread:", m_worker.GetThreadName( tidAlloc ) ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( tidAlloc ) ); + if( m_worker.IsThreadFiber( tidAlloc ) ) + { + ImGui::SameLine(); + TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" ); + } + if( ev.CsAlloc() != 0 ) + { + const auto cs = ev.CsAlloc(); + SmallCallstackButton( ICON_FA_ALIGN_JUSTIFY, cs, idx ); + ImGui::SameLine(); + DrawCallstackCalls( cs, 2 ); + } + if( ev.TimeFree() < 0 ) + { + TextDisabledUnformatted( "Allocation still active" ); + } + else + { + TextFocused( "Freed at", TimeToStringExact( ev.TimeFree() ) ); + if( ImGui::IsItemClicked() ) CenterAtTime( ev.TimeFree() ); + ImGui::SameLine(); ImGui::Spacing(); ImGui::SameLine(); + SmallColorBox( GetThreadColor( tidFree, 0 ) ); + ImGui::SameLine(); + TextFocused( "Thread:", m_worker.GetThreadName( tidFree ) ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( tidFree ) ); + if( m_worker.IsThreadFiber( tidFree ) ) + { + ImGui::SameLine(); + TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" ); + } + if( ev.csFree.Val() != 0 ) + { + const auto cs = ev.csFree.Val(); + SmallCallstackButton( ICON_FA_ALIGN_JUSTIFY, cs, idx ); + ImGui::SameLine(); + DrawCallstackCalls( cs, 2 ); + } + TextFocused( "Duration:", TimeToString( ev.TimeFree() - ev.TimeAlloc() ) ); + } + + bool sep = false; + auto zoneAlloc = FindZoneAtTime( tidAlloc, ev.TimeAlloc() ); + if( zoneAlloc ) + { + ImGui::Separator(); + sep = true; + const auto& srcloc = m_worker.GetSourceLocation( zoneAlloc->SrcLoc() ); + const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); + ImGui::PushID( idx++ ); + TextFocused( "Zone alloc:", txt ); + auto hover = ImGui::IsItemHovered(); + ImGui::PopID(); + if( ImGui::IsItemClicked() ) + { + ShowZoneInfo( *zoneAlloc ); + } + if( hover ) + { + m_zoneHighlight = zoneAlloc; + if( IsMouseClicked( 2 ) ) + { + ZoomToZone( *zoneAlloc ); + } + ZoneTooltip( *zoneAlloc ); + } + } + + if( ev.TimeFree() >= 0 ) + { + auto zoneFree = FindZoneAtTime( tidFree, ev.TimeFree() ); + if( zoneFree ) + { + if( !sep ) ImGui::Separator(); + const auto& srcloc = m_worker.GetSourceLocation( zoneFree->SrcLoc() ); + const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); + TextFocused( "Zone free:", txt ); + auto hover = ImGui::IsItemHovered(); + if( ImGui::IsItemClicked() ) + { + ShowZoneInfo( *zoneFree ); + } + if( hover ) + { + m_zoneHighlight = zoneFree; + if( IsMouseClicked( 2 ) ) + { + ZoomToZone( *zoneFree ); + } + ZoneTooltip( *zoneFree ); + } + if( zoneAlloc != 0 && zoneAlloc == zoneFree ) + { + ImGui::SameLine(); + TextDisabledUnformatted( "(same zone)" ); + } + } + } + } + ImGui::End(); + if( !show ) m_memoryAllocInfoWindow = -1; +} + +void View::ListMemData( std::vector& vec, std::function DrawAddress, const char* id, int64_t startTime, uint64_t pool ) +{ + if( startTime == -1 ) startTime = 0; + if( ImGui::BeginTable( "##mem", 8, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY, ImVec2( 0, ImGui::GetTextLineHeightWithSpacing() * std::min( 1+vec.size(), 15 ) ) ) ) + { + ImGui::TableSetupScrollFreeze( 0, 1 ); + ImGui::TableSetupColumn( "Address", ImGuiTableColumnFlags_NoHide ); + ImGui::TableSetupColumn( "Size", ImGuiTableColumnFlags_PreferSortDescending ); + ImGui::TableSetupColumn( "Appeared at", ImGuiTableColumnFlags_DefaultSort ); + ImGui::TableSetupColumn( "Duration", ImGuiTableColumnFlags_PreferSortDescending ); + ImGui::TableSetupColumn( "Thread", ImGuiTableColumnFlags_NoSort ); + ImGui::TableSetupColumn( "Zone alloc", ImGuiTableColumnFlags_NoSort ); + ImGui::TableSetupColumn( "Zone free", ImGuiTableColumnFlags_NoSort ); + ImGui::TableSetupColumn( "Call stack", ImGuiTableColumnFlags_NoSort ); + ImGui::TableHeadersRow(); + + const auto& mem = m_worker.GetMemoryNamed( pool ); + const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs; + switch( sortspec.ColumnIndex ) + { + case 0: + if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) + { + pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return l->Ptr() < r->Ptr(); } ); + } + else + { + pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return l->Ptr() > r->Ptr(); } ); + } + break; + case 1: + if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) + { + pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return l->Size() < r->Size(); } ); + } + else + { + pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return l->Size() > r->Size(); } ); + } + break; + case 2: + if( sortspec.SortDirection == ImGuiSortDirection_Descending ) + { + std::reverse( vec.begin(), vec.end() ); + } + break; + case 3: + if( sortspec.SortDirection == ImGuiSortDirection_Ascending ) + { + pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return ( l->TimeFree() - l->TimeAlloc() ) < ( r->TimeFree() - r->TimeAlloc() ); } ); + } + else + { + pdqsort_branchless( vec.begin(), vec.end(), []( const auto& l, const auto& r ) { return ( l->TimeFree() - l->TimeAlloc() ) > ( r->TimeFree() - r->TimeAlloc() ); } ); + } + break; + default: + assert( false ); + break; + } + + int idx = 0; + ImGuiListClipper clipper; + clipper.Begin( vec.end() - vec.begin() ); + while( clipper.Step() ) + { + for( auto i=clipper.DisplayStart; iTimeAlloc(), v->TimeFree() >= 0 ? v->TimeFree() : m_worker.GetLastTime() ); + } + if( ImGui::IsItemHovered() ) + { + m_memoryAllocHover = arrIdx; + m_memoryAllocHoverWait = 2; + m_memoryAllocHoverPool = pool; + } + ImGui::TableNextColumn(); + ImGui::TextUnformatted( MemSizeToString( v->Size() ) ); + ImGui::TableNextColumn(); + ImGui::PushID( idx++ ); + if( ImGui::Selectable( TimeToStringExact( v->TimeAlloc() - startTime ) ) ) + { + CenterAtTime( v->TimeAlloc() ); + } + ImGui::PopID(); + ImGui::TableNextColumn(); + if( v->TimeFree() < 0 ) + { + TextColoredUnformatted( ImVec4( 0.6f, 1.f, 0.6f, 1.f ), TimeToString( m_worker.GetLastTime() - v->TimeAlloc() ) ); + ImGui::TableNextColumn(); + const auto tid = m_worker.DecompressThread( v->ThreadAlloc() ); + SmallColorBox( GetThreadColor( tid, 0 ) ); + ImGui::SameLine(); + ImGui::TextUnformatted( m_worker.GetThreadName( tid ) ); + } + else + { + ImGui::PushID( idx++ ); + if( ImGui::Selectable( TimeToString( v->TimeFree() - v->TimeAlloc() ) ) ) + { + CenterAtTime( v->TimeFree() ); + } + ImGui::PopID(); + ImGui::TableNextColumn(); + if( v->ThreadAlloc() == v->ThreadFree() ) + { + const auto tid = m_worker.DecompressThread( v->ThreadAlloc() ); + SmallColorBox( GetThreadColor( tid, 0 ) ); + ImGui::SameLine(); + ImGui::TextUnformatted( m_worker.GetThreadName( tid ) ); + } + else + { + const auto tidAlloc = m_worker.DecompressThread( v->ThreadAlloc() ); + const auto tidFree = m_worker.DecompressThread( v->ThreadFree() ); + SmallColorBox( GetThreadColor( tidAlloc, 0 ) ); + ImGui::SameLine(); + ImGui::TextUnformatted( m_worker.GetThreadName( tidAlloc ) ); + ImGui::SameLine(); + ImGui::TextUnformatted( "/" ); + ImGui::SameLine(); + SmallColorBox( GetThreadColor( tidFree, 0 ) ); + ImGui::SameLine(); + ImGui::TextUnformatted( m_worker.GetThreadName( tidFree ) ); + } + } + ImGui::TableNextColumn(); + auto zone = FindZoneAtTime( m_worker.DecompressThread( v->ThreadAlloc() ), v->TimeAlloc() ); + if( !zone ) + { + ImGui::TextUnformatted( "-" ); + } + else + { + const auto& srcloc = m_worker.GetSourceLocation( zone->SrcLoc() ); + const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); + ImGui::PushID( idx++ ); + auto sel = ImGui::Selectable( txt, m_zoneInfoWindow == zone ); + auto hover = ImGui::IsItemHovered(); + ImGui::PopID(); + if( sel ) + { + ShowZoneInfo( *zone ); + } + if( hover ) + { + m_zoneHighlight = zone; + if( IsMouseClicked( 2 ) ) + { + ZoomToZone( *zone ); + } + ZoneTooltip( *zone ); + } + } + ImGui::TableNextColumn(); + if( v->TimeFree() < 0 ) + { + TextColoredUnformatted( ImVec4( 0.6f, 1.f, 0.6f, 1.f ), "active" ); + } + else + { + auto zoneFree = FindZoneAtTime( m_worker.DecompressThread( v->ThreadFree() ), v->TimeFree() ); + if( !zoneFree ) + { + ImGui::TextUnformatted( "-" ); + } + else + { + const auto& srcloc = m_worker.GetSourceLocation( zoneFree->SrcLoc() ); + const auto txt = srcloc.name.active ? m_worker.GetString( srcloc.name ) : m_worker.GetString( srcloc.function ); + ImGui::PushID( idx++ ); + bool sel; + if( zoneFree == zone ) + { + ImGui::PushStyleColor( ImGuiCol_Text, ImVec4( 1.f, 1.f, 0.6f, 1.f ) ); + sel = ImGui::Selectable( txt, m_zoneInfoWindow == zoneFree ); + ImGui::PopStyleColor( 1 ); + } + else + { + sel = ImGui::Selectable( txt, m_zoneInfoWindow == zoneFree ); + } + auto hover = ImGui::IsItemHovered(); + ImGui::PopID(); + if( sel ) + { + ShowZoneInfo( *zoneFree ); + } + if( hover ) + { + m_zoneHighlight = zoneFree; + if( IsMouseClicked( 2 ) ) + { + ZoomToZone( *zoneFree ); + } + ZoneTooltip( *zoneFree ); + } + } + } + ImGui::TableNextColumn(); + if( v->CsAlloc() == 0 ) + { + TextDisabledUnformatted( "[alloc]" ); + } + else + { + SmallCallstackButton( "alloc", v->CsAlloc(), idx ); + } + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + if( v->csFree.Val() == 0 ) + { + TextDisabledUnformatted( "[free]" ); + } + else + { + SmallCallstackButton( "free", v->csFree.Val(), idx ); + } + } + } + ImGui::EndTable(); + } +} + +}