diff --git a/src/PackLib/PackProfile.cpp b/src/PackLib/PackProfile.cpp index cae8dab..6a51924 100644 --- a/src/PackLib/PackProfile.cpp +++ b/src/PackLib/PackProfile.cpp @@ -4,12 +4,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include @@ -24,6 +26,7 @@ using Clock = std::chrono::steady_clock; constexpr const char* kPackProfileEnv = "M2PACK_PROFILE"; constexpr const char* kPackProfileOutput = "log/pack_profile.txt"; constexpr std::size_t kTopListLimit = 12; +constexpr auto kSnapshotFlushInterval = std::chrono::seconds(1); struct LoadStats { @@ -69,10 +72,15 @@ struct PackProfileState std::map mount_by_format; std::map mount_by_pack; std::map stage_by_key; + bool snapshot_dirty = false; + bool stop_snapshot_thread = false; + std::condition_variable snapshot_cv; + std::thread snapshot_thread; std::mutex mutex; ~PackProfileState() { + StopSnapshotThread(); if (enabled) { std::lock_guard lock(mutex); @@ -80,6 +88,50 @@ struct PackProfileState } } + void StartSnapshotThread() + { + snapshot_thread = std::thread([this]() { + SnapshotLoop(); + }); + } + + void StopSnapshotThread() + { + { + std::lock_guard lock(mutex); + stop_snapshot_thread = true; + } + snapshot_cv.notify_all(); + if (snapshot_thread.joinable()) + { + snapshot_thread.join(); + } + } + + void SnapshotLoop() + { + std::unique_lock lock(mutex); + for (;;) + { + const bool shouldStop = snapshot_cv.wait_for( + lock, + kSnapshotFlushInterval, + [this]() { return stop_snapshot_thread; }); + if (shouldStop) + { + return; + } + + if (!enabled || !snapshot_dirty) + { + continue; + } + + WriteReportLocked("periodic"); + snapshot_dirty = false; + } + } + void WriteReportLocked(std::string_view reason) const { std::error_code ec; @@ -376,6 +428,9 @@ void InitializePackProfile(const char* commandLine) .elapsed_us = 0, }); g_pack_profile.WriteReportLocked("initialized"); + g_pack_profile.snapshot_dirty = false; + g_pack_profile.stop_snapshot_thread = false; + g_pack_profile.StartSnapshotThread(); } bool IsPackProfileEnabled() @@ -402,6 +457,7 @@ void MarkPackProfilePhase(std::string_view phase) .elapsed_us = CurrentElapsedUs(), }); g_pack_profile.WriteReportLocked(PackProfileState::MakePhaseKey("phase", phase)); + g_pack_profile.snapshot_dirty = false; } void RecordPackProfileMount( @@ -429,6 +485,7 @@ void RecordPackProfileMount( packStats.failures += ok ? 0 : 1; packStats.entry_count += entryCount; packStats.elapsed_us += elapsedUs; + g_pack_profile.snapshot_dirty = true; } void RecordPackProfileLoad( @@ -459,6 +516,7 @@ void RecordPackProfileLoad( update(g_pack_profile.load_by_phase_pack[ PackProfileState::MakePhaseKey(g_pack_profile.current_phase, PackProfileState::ParsePackLabel(packPath))]); update(g_pack_profile.load_by_extension[PackProfileState::ParseExtension(requestPath)]); + g_pack_profile.snapshot_dirty = true; } void RecordPackProfileStage( @@ -480,6 +538,7 @@ void RecordPackProfileStage( stats.input_bytes += inputBytes; stats.output_bytes += outputBytes; stats.elapsed_us += elapsedUs; + g_pack_profile.snapshot_dirty = true; } void FlushPackProfileSnapshot(std::string_view reason) @@ -491,4 +550,5 @@ void FlushPackProfileSnapshot(std::string_view reason) std::lock_guard lock(g_pack_profile.mutex); g_pack_profile.WriteReportLocked(reason); + g_pack_profile.snapshot_dirty = false; }