From 72c87715bf52fa861142044719a2bdfa77346646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Wed, 30 Jul 2025 15:39:50 +0200 Subject: [PATCH 1/3] Implement connection time limit --- profiler/src/main.cpp | 20 +++++++++++++++-- profiler/src/profiler/TracyConfig.cpp | 6 +++++ profiler/src/profiler/TracyConfig.hpp | 2 ++ profiler/src/profiler/TracyView.cpp | 32 ++++++++++++++++++++++----- server/TracyWorker.cpp | 9 ++++++-- server/TracyWorker.hpp | 7 +++++- 6 files changed, 66 insertions(+), 10 deletions(-) diff --git a/profiler/src/main.cpp b/profiler/src/main.cpp index 7159ca31fb..5d86f85952 100644 --- a/profiler/src/main.cpp +++ b/profiler/src/main.cpp @@ -777,6 +777,15 @@ static void DrawContents() ImGui::EndDisabled(); } + ImGui::Spacing(); + if( ImGui::Checkbox( "Time limit", &tracy::s_config.timeLimit ) ) tracy::SaveConfig(); + ImGui::SameLine(); + tracy::DrawHelpMarker( "When enabled, the profiler will stop recording data once the connection duration exceeds the specified value (in seconds). Note that the capture duration itself is not correlated to the connection duration." ); + ImGui::SameLine(); + ImGui::SetNextItemWidth( 70 * dpiScale ); + if( ImGui::InputFloat( "##timelimit", &tracy::s_config.connectionTimeLimitSeconds, 0.f, 0.f, "%.2f" ) ) { tracy::s_config.connectionTimeLimitSeconds = std::max( tracy::s_config.connectionTimeLimitSeconds, 0.f ); tracy::SaveConfig(); } + ImGui::SameLine(); + ImGui::TextUnformatted( "s" ); ImGui::Spacing(); if( ImGui::Checkbox( "Enable achievements", &tracy::s_config.achievements ) ) tracy::SaveConfig(); ImGui::Spacing(); @@ -970,11 +979,18 @@ static void DrawContents() } } } - if( tracy::s_config.memoryLimit ) + if( tracy::s_config.memoryLimit || tracy::s_config.timeLimit ) { ImGui::SameLine(); tracy::TextColoredUnformatted( 0xFF00FFFF, ICON_FA_TRIANGLE_EXCLAMATION ); - tracy::TooltipIfHovered( "Memory limit is active" ); + if( tracy::s_config.memoryLimit ) + { + tracy::TooltipIfHovered( "Memory limit is active" ); + } + if( tracy::s_config.timeLimit ) + { + tracy::TooltipIfHovered( "Capture duration limit is active" ); + } } ImGui::SameLine( 0, ImGui::GetTextLineHeight() * 2 ); diff --git a/profiler/src/profiler/TracyConfig.cpp b/profiler/src/profiler/TracyConfig.cpp index aaeaf30825..387f5356a1 100644 --- a/profiler/src/profiler/TracyConfig.cpp +++ b/profiler/src/profiler/TracyConfig.cpp @@ -30,6 +30,8 @@ void LoadConfig() if( ini_sget( ini, "timeline", "verticalScrollMultiplier", "%lf", &v1 ) && v1 > 0.0 ) s_config.verticalScrollMultiplier = v1; if( ini_sget( ini, "memory", "limit", "%d", &v ) ) s_config.memoryLimit = v; if( ini_sget( ini, "memory", "percent", "%d", &v ) && v >= 1 && v < 1000 ) s_config.memoryLimitPercent = v; + if( ini_sget( ini, "connection", "durationLimitEnabled", "%d", &v ) && v >= 1 ) s_config.timeLimit = v; + if( ini_sget( ini, "connection", "durationLimitInSeconds", "%lf", &v1 ) ) s_config.connectionTimeLimitSeconds = v1; if( ini_sget( ini, "achievements", "enabled", "%d", &v ) ) s_config.achievements = v; if( ini_sget( ini, "achievements", "asked", "%d", &v ) ) s_config.achievementsAsked = v; if( ini_sget( ini, "ui", "saveUserScale", "%d", &v ) ) s_config.saveUserScale = v; @@ -67,6 +69,10 @@ bool SaveConfig() fprintf( f, "limit = %i\n", (int)s_config.memoryLimit ); fprintf( f, "percent = %i\n", s_config.memoryLimitPercent ); + fprintf( f, "\n[connection]\n" ); + fprintf( f, "durationLimitEnabled = %i\n", (int)s_config.timeLimit ); + fprintf( f, "durationLimitInSeconds = %f\n", s_config.connectionTimeLimitSeconds ); + fprintf( f, "\n[achievements]\n" ); fprintf( f, "enabled = %i\n", (int)s_config.achievements ); fprintf( f, "asked = %i\n", (int)s_config.achievementsAsked ); diff --git a/profiler/src/profiler/TracyConfig.hpp b/profiler/src/profiler/TracyConfig.hpp index dca00a6412..e553d06ba3 100644 --- a/profiler/src/profiler/TracyConfig.hpp +++ b/profiler/src/profiler/TracyConfig.hpp @@ -17,6 +17,8 @@ struct Config double verticalScrollMultiplier = 1.0; bool memoryLimit = false; int memoryLimitPercent = 80; + bool timeLimit = false; + float connectionTimeLimitSeconds = 10.f; bool achievements = false; bool achievementsAsked = false; int dynamicColors = 1; diff --git a/profiler/src/profiler/TracyView.cpp b/profiler/src/profiler/TracyView.cpp index ee970358f7..1ea7fecae9 100644 --- a/profiler/src/profiler/TracyView.cpp +++ b/profiler/src/profiler/TracyView.cpp @@ -38,7 +38,7 @@ namespace tracy double s_time = 0; View::View( void(*cbMainThread)(const std::function&, bool), const char* addr, uint16_t port, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, AchievementsMgr* amgr ) - : m_worker( addr, port, s_config.memoryLimit == 0 ? -1 : ( s_config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100 ) ) + : m_worker( addr, port, s_config.memoryLimit == 0 ? -1 : ( s_config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100 ), s_config.timeLimit == 0 ? -1 : s_config.maxDurationSeconds ) , m_staticView( false ) , m_viewMode( ViewMode::LastFrames ) , m_viewModeHeuristicTry( true ) @@ -1055,7 +1055,27 @@ bool View::DrawImpl() ImGui::SameLine(); dx = ImGui::GetCursorPosX() - cx; if( dx < targetLabelSize ) ImGui::SameLine( cx + targetLabelSize ); - + cx = ImGui::GetCursorPosX(); + auto now = std::chrono::steady_clock::now(); + const auto timeLimit = m_worker.GetTimeLimit(); + const auto timeLimitReached = timeLimit.count() > 0.f && ( now > m_worker.GetStartTime() + timeLimit ); + if( timeLimitReached ) + { + ImGui::Text( ICON_FA_CLOCK " %.2f s", timeLimit.count() ); + } + else + { + ImGui::Text( ICON_FA_CLOCK " %.2f s", std::chrono::duration_cast( now - m_worker.GetStartTime() ).count() / 1000.f ); + } + if( ImGui::IsItemHovered() ) + { + ImGui::BeginTooltip(); + ImGui::Text( "Capture time" ); + ImGui::EndTooltip(); + } + ImGui::SameLine(); + dx = ImGui::GetCursorPosX() - cx; + if( dx < targetLabelSize ) ImGui::SameLine( cx + targetLabelSize ); targetLabelSize = ImGui::CalcTextSize( ICON_FA_MEMORY " 1234.56 MB (123.45 %%)" ).x; cx = ImGui::GetCursorPosX(); const auto mem = memUsage.load( std::memory_order_relaxed ); @@ -1080,10 +1100,11 @@ bool View::DrawImpl() ImGui::Spacing(); const auto memoryLimit = m_worker.GetMemoryLimit(); - if( memoryLimit > 0 ) + const auto memoryLimitReached = memoryLimit > 0 && memUsage.load( std::memory_order_relaxed ) > memoryLimit; + if( memoryLimit > 0 || timeLimit.count() > 0.f ) { ImGui::SameLine(); - if( memUsage.load( std::memory_order_relaxed ) > memoryLimit ) + if( memoryLimitReached || timeLimitReached ) { TextColoredUnformatted( 0xFF2222FF, ICON_FA_TRIANGLE_EXCLAMATION ); } @@ -1094,7 +1115,8 @@ bool View::DrawImpl() if( ImGui::IsItemHovered() ) { ImGui::BeginTooltip(); - ImGui::Text( "Memory limit: %s", MemSizeToString( memoryLimit ) ); + if( memoryLimit > 0 ) ImGui::Text( "Memory limit: %s", MemSizeToString( memoryLimit ) ); + if( timeLimit.count() > 0.f ) ImGui::Text("Capture time limit: %.2f s", timeLimit.count()); ImGui::EndTooltip(); } } diff --git a/server/TracyWorker.cpp b/server/TracyWorker.cpp index 483e3233d0..4d5874766e 100644 --- a/server/TracyWorker.cpp +++ b/server/TracyWorker.cpp @@ -255,7 +255,7 @@ static bool IsQueryPrio( ServerQuery type ) LoadProgress Worker::s_loadProgress; -Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit ) +Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit, float timeLimit ) : m_addr( addr ) , m_port( port ) , m_hasData( false ) @@ -272,6 +272,7 @@ Worker::Worker( const char* addr, uint16_t port, int64_t memoryLimit ) , m_pendingCallstackSubframes( 0 ) , m_pendingSymbolCode( 0 ) , m_memoryLimit( memoryLimit ) + , m_timeLimit( timeLimit ) , m_callstackFrameStaging( nullptr ) , m_traceVersion( CurrentVersion ) , m_loadTime( 0 ) @@ -313,6 +314,7 @@ Worker::Worker( const char* name, const char* program, const std::vector 0 && memUsage.load( std::memory_order_relaxed ) > m_memoryLimit ) ) + if( m_shutdown.load( std::memory_order_relaxed ) + || ( m_memoryLimit > 0 && memUsage.load( std::memory_order_relaxed ) > m_memoryLimit ) + || ( m_timeLimit.count() > 0.f && std::chrono::steady_clock::now() > m_data.startTime + m_timeLimit ) ) { QueryTerminate(); goto close; diff --git a/server/TracyWorker.hpp b/server/TracyWorker.hpp index 2aa09a0eb3..b424a9dacb 100644 --- a/server/TracyWorker.hpp +++ b/server/TracyWorker.hpp @@ -407,6 +407,8 @@ class Worker bool hasBranchRetirement = false; unordered_flat_map fiberToThreadMap; + + std::chrono::time_point startTime; }; struct MbpsBlock @@ -455,7 +457,7 @@ class Worker NUM_FAILURES }; - Worker( const char* addr, uint16_t port, int64_t memoryLimit ); + Worker( const char* addr, uint16_t port, int64_t memoryLimit, float timeLimit ); Worker( const char* name, const char* program, const std::vector& timeline, const std::vector& messages, const std::vector& plots, const std::unordered_map& threadNames ); Worker( FileRead& f, EventType::Type eventMask = EventType::All, bool bgTasks = true, bool allowStringModification = false); ~Worker(); @@ -671,6 +673,8 @@ class Worker void Disconnect(); bool WasDisconnectIssued() const { return m_disconnect; } int64_t GetMemoryLimit() const { return m_memoryLimit; } + std::chrono::duration GetTimeLimit() const { return m_timeLimit; } + std::chrono::steady_clock::time_point GetStartTime() const { return m_data.startTime; } void Write( FileWrite& f, bool fiDict ); int GetTraceVersion() const { return m_traceVersion; } @@ -1068,6 +1072,7 @@ class Worker Slab<64*1024*1024> m_slab; int64_t m_memoryLimit; + std::chrono::duration m_timeLimit; DataBlock m_data; MbpsBlock m_mbpsData; From 40715f1722b904f6bf2fd84681dfa6b42de5f970 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Wed, 30 Jul 2025 15:45:37 +0200 Subject: [PATCH 2/3] Remove the unneeded `View::m_disconnectIssued` boolean --- profiler/src/profiler/TracyView.cpp | 6 +++--- profiler/src/profiler/TracyView.hpp | 1 - profiler/src/profiler/TracyView_ConnectionState.cpp | 3 +-- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/profiler/src/profiler/TracyView.cpp b/profiler/src/profiler/TracyView.cpp index 1ea7fecae9..b37691e2b1 100644 --- a/profiler/src/profiler/TracyView.cpp +++ b/profiler/src/profiler/TracyView.cpp @@ -38,7 +38,7 @@ namespace tracy double s_time = 0; View::View( void(*cbMainThread)(const std::function&, bool), const char* addr, uint16_t port, SetTitleCallback stcb, SetScaleCallback sscb, AttentionCallback acb, AchievementsMgr* amgr ) - : m_worker( addr, port, s_config.memoryLimit == 0 ? -1 : ( s_config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100 ), s_config.timeLimit == 0 ? -1 : s_config.maxDurationSeconds ) + : m_worker( addr, port, s_config.memoryLimit == 0 ? -1 : ( s_config.memoryLimitPercent * tracy::GetPhysicalMemorySize() / 100 ), s_config.timeLimit == 0 ? -1 : s_config.connectionTimeLimitSeconds ) , m_staticView( false ) , m_viewMode( ViewMode::LastFrames ) , m_viewModeHeuristicTry( true ) @@ -825,9 +825,9 @@ bool View::DrawImpl() ImGui::SameLine(); if( ImGui::BeginPopup( "TracyConnectionPopup" ) ) { - const bool wasDisconnectIssued = m_disconnectIssued; + const bool wasDisconnectIssued = m_worker.WasDisconnectIssued(); const bool discardData = !DrawConnection(); - const bool disconnectIssuedJustNow = m_disconnectIssued != wasDisconnectIssued; + const bool disconnectIssuedJustNow = m_worker.WasDisconnectIssued() != wasDisconnectIssued; if( discardData ) keepOpen = false; if( disconnectIssuedJustNow || discardData ) ImGui::CloseCurrentPopup(); ImGui::EndPopup(); diff --git a/profiler/src/profiler/TracyView.hpp b/profiler/src/profiler/TracyView.hpp index 272b187869..2a552c61cc 100644 --- a/profiler/src/profiler/TracyView.hpp +++ b/profiler/src/profiler/TracyView.hpp @@ -498,7 +498,6 @@ class View size_t m_prevMessages = 0; bool m_messagesShowCallstack = false; Vector m_msgList; - bool m_disconnectIssued = false; uint64_t m_selectedThread = 0; DecayValue m_drawThreadMigrations = 0; DecayValue m_drawThreadHighlight = 0; diff --git a/profiler/src/profiler/TracyView_ConnectionState.cpp b/profiler/src/profiler/TracyView_ConnectionState.cpp index 77c18ac00d..fcb658e9ab 100644 --- a/profiler/src/profiler/TracyView_ConnectionState.cpp +++ b/profiler/src/profiler/TracyView_ConnectionState.cpp @@ -143,12 +143,11 @@ bool View::DrawConnection() ImGui::SameLine( 0, 2 * ty ); const char* stopStr = ICON_FA_PLUG " Stop"; Worker::MainThreadDataLockGuard lock = m_worker.ObtainLockForMainThread(); - if( !m_disconnectIssued && m_worker.IsConnected() ) + if( !m_worker.WasDisconnectIssued() && m_worker.IsConnected() ) { if( ImGui::Button( stopStr ) ) { m_worker.Disconnect(); - m_disconnectIssued = true; } } else From 44cbfa987625e190fc6b0a3e764bd2f53d8c8e17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Gr=C3=A9goire?= Date: Sat, 9 Aug 2025 21:47:20 +0200 Subject: [PATCH 3/3] Remove connection time from main ui, add time/memory remaining in warning --- profiler/src/profiler/TracyView.cpp | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/profiler/src/profiler/TracyView.cpp b/profiler/src/profiler/TracyView.cpp index b37691e2b1..863c1e5b2c 100644 --- a/profiler/src/profiler/TracyView.cpp +++ b/profiler/src/profiler/TracyView.cpp @@ -1055,27 +1055,7 @@ bool View::DrawImpl() ImGui::SameLine(); dx = ImGui::GetCursorPosX() - cx; if( dx < targetLabelSize ) ImGui::SameLine( cx + targetLabelSize ); - cx = ImGui::GetCursorPosX(); - auto now = std::chrono::steady_clock::now(); - const auto timeLimit = m_worker.GetTimeLimit(); - const auto timeLimitReached = timeLimit.count() > 0.f && ( now > m_worker.GetStartTime() + timeLimit ); - if( timeLimitReached ) - { - ImGui::Text( ICON_FA_CLOCK " %.2f s", timeLimit.count() ); - } - else - { - ImGui::Text( ICON_FA_CLOCK " %.2f s", std::chrono::duration_cast( now - m_worker.GetStartTime() ).count() / 1000.f ); - } - if( ImGui::IsItemHovered() ) - { - ImGui::BeginTooltip(); - ImGui::Text( "Capture time" ); - ImGui::EndTooltip(); - } - ImGui::SameLine(); - dx = ImGui::GetCursorPosX() - cx; - if( dx < targetLabelSize ) ImGui::SameLine( cx + targetLabelSize ); + targetLabelSize = ImGui::CalcTextSize( ICON_FA_MEMORY " 1234.56 MB (123.45 %%)" ).x; cx = ImGui::GetCursorPosX(); const auto mem = memUsage.load( std::memory_order_relaxed ); @@ -1099,8 +1079,12 @@ bool View::DrawImpl() if( dx < targetLabelSize ) ImGui::SameLine( cx + targetLabelSize ); ImGui::Spacing(); + const auto timeLimit = m_worker.GetTimeLimit(); + const auto timeRemaining = timeLimit - ( std::chrono::steady_clock::now() - m_worker.GetStartTime() ); + const bool timeLimitReached = timeLimit.count() > 0 && timeRemaining.count() < 0; const auto memoryLimit = m_worker.GetMemoryLimit(); - const auto memoryLimitReached = memoryLimit > 0 && memUsage.load( std::memory_order_relaxed ) > memoryLimit; + const auto memoryRemainingBeforeLimit = memoryLimit - memUsage.load( std::memory_order_relaxed ); + const bool memoryLimitReached = memoryLimit > 0 && memoryRemainingBeforeLimit < 0; if( memoryLimit > 0 || timeLimit.count() > 0.f ) { ImGui::SameLine(); @@ -1115,8 +1099,24 @@ bool View::DrawImpl() if( ImGui::IsItemHovered() ) { ImGui::BeginTooltip(); - if( memoryLimit > 0 ) ImGui::Text( "Memory limit: %s", MemSizeToString( memoryLimit ) ); - if( timeLimit.count() > 0.f ) ImGui::Text("Capture time limit: %.2f s", timeLimit.count()); + if( memoryLimit > 0 ) + { + ImGui::Text( "Memory limit: %s", MemSizeToString( memoryLimit ) ); + if( !memoryLimitReached ) + { + ImGui::SameLine(); + ImGui::Text( " (%s remaining)", MemSizeToString( memoryRemainingBeforeLimit ) ); + } + } + if( timeLimit.count() > 0.f ) + { + ImGui::Text( "Capture time limit: %.2f s", timeLimit.count() ); + if( !timeLimitReached ) + { + ImGui::SameLine(); + ImGui::Text( " (%.2f s remaining)", std::chrono::duration( timeRemaining ).count() ); + } + } ImGui::EndTooltip(); } }