< Summary

Information
Class: SwitchBlade.Services.MemoryDiagnosticsService
Assembly: SwitchBlade
File(s): D:\a\switchblade\switchblade\Services\MemoryDiagnosticsService.cs
Tag: 203_23722840422
Line coverage
100%
Covered lines: 83
Uncovered lines: 0
Coverable lines: 83
Total lines: 134
Line coverage: 100%
Branch coverage
100%
Covered branches: 22
Total branches: 22
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%1818100%
StartAsync(...)100%11100%
StopAsync()100%22100%
RunDiagnosticsLoop()100%22100%
ForceLogMemoryStats()100%11100%
FormatBytes(...)100%11100%
Dispose()100%11100%

File(s)

D:\a\switchblade\switchblade\Services\MemoryDiagnosticsService.cs

#LineLine coverage
 1using System;
 2using System.Diagnostics;
 3using System.Threading;
 4using System.Threading.Tasks;
 5using SwitchBlade.Contracts;
 6using SwitchBlade.Core;
 7
 8namespace SwitchBlade.Services
 9{
 10    /// <summary>
 11    /// Background service for tracking memory usage and cache statistics to diagnose leaks.
 12    /// Runs every 60 seconds.
 13    /// </summary>
 14    public class MemoryDiagnosticsService : IDisposable
 15    {
 16        private readonly IPeriodicTimer _timer;
 17        private readonly CancellationTokenSource _cts;
 18        private Task? _executionTask;
 19
 20        private readonly IWindowOrchestrationService _orchestrationService;
 21        private readonly IIconService _iconService;
 22        private readonly IWindowSearchService _searchService;
 23        private readonly ILogger _logger;
 24        private readonly IProcessFactory _processFactory;
 25        private readonly IMemoryInfoProvider _memoryInfoProvider;
 26
 1427        public MemoryDiagnosticsService(
 1428            IWindowOrchestrationService orchestrationService,
 1429            IIconService iconService,
 1430            IWindowSearchService searchService,
 1431            ILogger logger,
 1432            IProcessFactory? processFactory = null,
 1433            IMemoryInfoProvider? memoryInfoProvider = null,
 1434            Func<TimeSpan, IPeriodicTimer>? timerFactory = null,
 1435            TimeSpan? interval = null)
 1436        {
 1437            _orchestrationService = orchestrationService ?? throw new ArgumentNullException(nameof(orchestrationService)
 1338            _iconService = iconService ?? throw new ArgumentNullException(nameof(iconService));
 1239            _searchService = searchService ?? throw new ArgumentNullException(nameof(searchService));
 1140            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
 1041            _processFactory = processFactory ?? new ProcessFactory(new SystemProcessProvider());
 1042            _memoryInfoProvider = memoryInfoProvider ?? new SystemMemoryInfoProvider();
 43
 1044            var timerInterval = interval ?? TimeSpan.FromSeconds(60);
 1045            _timer = timerFactory?.Invoke(timerInterval) ?? new SystemPeriodicTimer(timerInterval);
 1046            _cts = new CancellationTokenSource();
 1047        }
 48
 49        public Task StartAsync(CancellationToken cancellationToken)
 450        {
 451            _ = cancellationToken;
 452            _logger.Log("MemoryDiagnosticsService starting...");
 453            _executionTask = RunDiagnosticsLoop();
 454            return Task.CompletedTask;
 455        }
 56
 57        public async Task StopAsync(CancellationToken cancellationToken)
 558        {
 559            _ = cancellationToken;
 560            _logger.Log("MemoryDiagnosticsService stopping...");
 561            _cts.Cancel();
 562            if (_executionTask != null)
 463            {
 64                try
 465                {
 466                    await _executionTask;
 267                }
 668                catch (OperationCanceledException) { }
 469            }
 570        }
 71
 72        private async Task RunDiagnosticsLoop()
 473        {
 74            // First delay before first tick (standard periodic timer behavior)
 75            try
 476            {
 577                while (await _timer.WaitForNextTickAsync(_cts.Token))
 178                {
 179                    ForceLogMemoryStats();
 180                }
 181            }
 382            catch (Exception ex) when (ex is not OperationCanceledException)
 183            {
 184                _logger.LogError("MemoryDiagnosticsService loop error", ex);
 185            }
 286        }
 87
 88        public void ForceLogMemoryStats()
 389        {
 90            try
 391            {
 92                // Force a check on the current process
 393                using var proc = _processFactory.GetCurrentProcess();
 294                proc.Refresh();
 95
 296                long managedMemory = _memoryInfoProvider.GetTotalMemory(false); // Bytes
 297                long workingSet = proc.WorkingSet64;           // Bytes (RAM)
 298                long privateBytes = proc.PrivateMemorySize64;  // Bytes (Committed)
 299                int handleCount = proc.HandleCount;
 2100                int threadCount = proc.ThreadCount;
 101
 102                // Cache Stats — using IDiagnosticsProvider.CacheCount via interfaces
 2103                int winCache = _orchestrationService.CacheCount;
 2104                int iconCache = _iconService.CacheCount;
 105
 2106                string msg = $"\n[MEM-DIAG] " +
 2107                             $"Managed: {FormatBytes(managedMemory)} | " +
 2108                             $"Private: {FormatBytes(privateBytes)} | " +
 2109                             $"WorkingSet: {FormatBytes(workingSet)} | " +
 2110                             $"Handles: {handleCount} | " +
 2111                             $"Threads: {threadCount} | " +
 2112                             $"caches(Win/Icon): {winCache}/{iconCache}";
 113
 2114                _logger.Log(msg);
 2115            }
 1116            catch (Exception ex)
 1117            {
 1118                _logger.LogError("Failed to log memory stats", ex);
 1119            }
 3120        }
 121
 122        private static string FormatBytes(long bytes)
 6123        {
 6124            return $"{bytes / 1024 / 1024} MB";
 6125        }
 126
 127        public void Dispose()
 11128        {
 11129            _cts.Dispose();
 11130            _timer.Dispose();
 11131            GC.SuppressFinalize(this);
 11132        }
 133    }
 134}