From 739b2be4c270a36383a5240f60e42d2db443145a Mon Sep 17 00:00:00 2001 From: Jason Ross Date: Wed, 3 Jun 2026 18:59:45 -0500 Subject: [PATCH] clean up and AMD parity check --- .../App.xaml.cs | 24 ++---- .../MainWindow.cs | 73 ++++--------------- .../Services/HardwareMonitorService.cs | 44 +++++------ .../Services/Tracing/FileStartupTracer.cs | 64 ++++------------ .../Services/Tracing/IStartupTracer.cs | 8 -- .../Services/Tracing/NoOpStartupTracer.cs | 20 ----- .../ViewModels/MainWindowViewModel.cs | 61 +++------------- LibreHardwareMonitorLib/Hardware/Computer.cs | 14 +--- .../Hardware/Cpu/Amd0FCpu.cs | 4 +- .../Hardware/Cpu/Amd17Cpu.cs | 6 +- .../Hardware/Cpu/CpuGroup.cs | 40 +--------- .../Hardware/Cpu/GenericCpu.cs | 14 +--- .../Hardware/Cpu/IntelCpu.cs | 14 +--- .../Hardware/HardwareStartupTrace.cs | 24 ++++++ .../Hardware/Memory/MemoryGroup.cs | 19 +---- docs/code-review-followup.md | 2 +- 16 files changed, 106 insertions(+), 325 deletions(-) diff --git a/LibreHardwareMonitor.Windows.WinUI/App.xaml.cs b/LibreHardwareMonitor.Windows.WinUI/App.xaml.cs index 14a1625..fda6977 100644 --- a/LibreHardwareMonitor.Windows.WinUI/App.xaml.cs +++ b/LibreHardwareMonitor.Windows.WinUI/App.xaml.cs @@ -25,20 +25,20 @@ public partial class App : Application { _startupTrace = StartupTracer.Create(); _startupTrace.Mark("App.Constructor.Begin"); - MeasureStartup("App.WireExceptionHandlers", () => + _startupTrace.Measure("App.WireExceptionHandlers", () => { UnhandledException += App_UnhandledException; AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; }); - MeasureStartup("App.InitializeComponent", InitializeComponent); + _startupTrace.Measure("App.InitializeComponent", InitializeComponent); _startupTrace.Mark("App.Constructor.Complete"); _startupTrace.Flush(); } protected override void OnLaunched(LaunchActivatedEventArgs args) { - MeasureStartup("App.OnLaunched", LaunchMainWindow); + _startupTrace.Measure("App.OnLaunched", LaunchMainWindow); } private void LaunchMainWindow() @@ -50,16 +50,16 @@ public partial class App : Application try { _startupTrace.Mark("App.LaunchMainWindow.Begin"); - _serviceProvider = MeasureStartup("App.BuildServiceProvider", () => + _serviceProvider = _startupTrace.Measure("App.BuildServiceProvider", () => new ServiceCollection().AddAppServices(_startupTrace).BuildServiceProvider()); - MainWindow mainWindow = MeasureStartup("App.CreateMainWindow", () => new MainWindow( + MainWindow mainWindow = _startupTrace.Measure("App.CreateMainWindow", () => new MainWindow( _serviceProvider.GetRequiredService(), _startupTrace, _serviceProvider.GetRequiredService(), _serviceProvider)); _window = mainWindow; - MeasureStartup("App.ActivateWindow", mainWindow.Activate); - MeasureStartup("App.StartMonitoringAfterActivation", mainWindow.StartMonitoringAfterActivation); + _startupTrace.Measure("App.ActivateWindow", mainWindow.Activate); + _startupTrace.Measure("App.StartMonitoringAfterActivation", mainWindow.StartMonitoringAfterActivation); _startupTrace.Mark("App.LaunchMainWindow.Complete"); _startupTrace.Flush(); } @@ -72,16 +72,6 @@ public partial class App : Application } } - private void MeasureStartup(string phase, Action action) - { - _startupTrace.Measure(phase, action); - } - - private T MeasureStartup(string phase, Func action) - { - return _startupTrace.Measure(phase, action); - } - private static void App_UnhandledException(object sender, Microsoft.UI.Xaml.UnhandledExceptionEventArgs args) { WriteExceptionLog("WinUI unhandled exception", args.Exception); diff --git a/LibreHardwareMonitor.Windows.WinUI/MainWindow.cs b/LibreHardwareMonitor.Windows.WinUI/MainWindow.cs index 0d61987..a6981f8 100644 --- a/LibreHardwareMonitor.Windows.WinUI/MainWindow.cs +++ b/LibreHardwareMonitor.Windows.WinUI/MainWindow.cs @@ -64,9 +64,9 @@ public sealed partial class MainWindow : Window _serviceProvider = serviceProvider; _startupTrace.Mark("MainWindow.Constructor.Begin"); - MeasureStartup("MainWindow.InitializeComponent", InitializeComponent); + _startupTrace.Measure("MainWindow.InitializeComponent", InitializeComponent); - MainWindowRuntime runtime = MeasureStartup( + MainWindowRuntime runtime = _startupTrace.Measure( "MainWindow.CreateRuntimeServices", () => runtimeFactory.Create(this, ViewModel, () => Content.XamlRoot, HideShowMainWindow)); _appWindow = runtime.AppWindow; @@ -78,7 +78,7 @@ public sealed partial class MainWindow : Window _columnMeasurer = runtime.ColumnMeasurer; _trayIconService.IsMainIconEnabled = ViewModel.MinimizeToTray; - MeasureStartup("MainWindow.ApplySavedDeviceColumnWidth", _columnMeasurer.ApplySavedWidth, () => FormattableString.Invariant($"width={_columnMeasurer.DeviceColumnWidth:F0}")); + _startupTrace.Measure("MainWindow.ApplySavedDeviceColumnWidth", _columnMeasurer.ApplySavedWidth, () => FormattableString.Invariant($"width={_columnMeasurer.DeviceColumnWidth:F0}")); _columnMeasurer.SettleTriggered += (_, _) => UpdateSensorColumnWidths(); TryApplyMicaBackdrop(); @@ -86,16 +86,16 @@ public sealed partial class MainWindow : Window RootGrid.LayoutUpdated += RootGrid_LayoutUpdated; Bind(ContentGrid, UIElement.IsHitTestVisibleProperty, ViewModel, nameof(ViewModel.IsHardwareInteractionEnabled)); - MeasureStartup("MainWindow.PopulateMenuSubmenus", PopulateMenuSubmenus); - MeasureStartup("MainWindow.PopulateSensorHeader", PopulateSensorHeader); - MeasureStartup("MainWindow.AttachPlotView", () => PlotControl.AttachViewModel(ViewModel)); + _startupTrace.Measure("MainWindow.PopulateMenuSubmenus", PopulateMenuSubmenus); + _startupTrace.Measure("MainWindow.PopulateSensorHeader", PopulateSensorHeader); + _startupTrace.Measure("MainWindow.AttachPlotView", () => PlotControl.AttachViewModel(ViewModel)); - MeasureStartup("MainWindow.RestoreWindowBounds", _placementService.Restore); - MeasureStartup("MainWindow.MaximizeWindow", _placementService.Maximize); - MeasureStartup("MainWindow.ApplyTheme", ApplyTheme); - MeasureStartup("MainWindow.UpdatePlotLayout", UpdatePlotLayout); + _startupTrace.Measure("MainWindow.RestoreWindowBounds", _placementService.Restore); + _startupTrace.Measure("MainWindow.MaximizeWindow", _placementService.Maximize); + _startupTrace.Measure("MainWindow.ApplyTheme", ApplyTheme); + _startupTrace.Measure("MainWindow.UpdatePlotLayout", UpdatePlotLayout); - _timer = MeasureStartup("MainWindow.CreateTimer", () => + _timer = _startupTrace.Measure("MainWindow.CreateTimer", () => { DispatcherQueueTimer timer = DispatcherQueue.CreateTimer(); timer.Interval = ViewModel.UpdateInterval; @@ -103,7 +103,7 @@ public sealed partial class MainWindow : Window return timer; }); - MeasureStartup("MainWindow.WireEvents", () => + _startupTrace.Measure("MainWindow.WireEvents", () => { ViewModel.PropertyChanged += (_, args) => { @@ -173,11 +173,11 @@ public sealed partial class MainWindow : Window { _startupTrace.Mark("MainWindow.StartMonitoringAfterActivation.Begin"); ApplyInitialWindowState(); - await MeasureStartupAsync("MainWindowViewModel.StartAsync", ViewModel.StartAsync); + await _startupTrace.MeasureAsync("MainWindowViewModel.StartAsync", ViewModel.StartAsync); SyncTraySensors(); SyncGadgetSensors(); UpdateGadgetVisibility(); - MeasureStartup("MainWindow.StartTimer", _timer.Start); + _startupTrace.Measure("MainWindow.StartTimer", _timer.Start); _startupTrace.Mark("MainWindow.StartMonitoringAfterActivation.Complete"); RequestStartupTraceComplete(); _startupTrace.Flush(); @@ -218,47 +218,6 @@ public sealed partial class MainWindow : Window _startupTrace.Flush(); } - private void MeasureStartup(string phase, Action action) - { - if (_startupTrace.IsComplete) - { - action(); - return; - } - - _startupTrace.Measure(phase, action); - } - - private void MeasureStartup(string phase, Action action, Func getDetail) - { - if (_startupTrace.IsComplete) - { - action(); - return; - } - - _startupTrace.Measure(phase, action, getDetail); - } - - private T MeasureStartup(string phase, Func action) - { - if (_startupTrace.IsComplete) - return action(); - - return _startupTrace.Measure(phase, action); - } - - private async Task MeasureStartupAsync(string phase, Func action) - { - if (_startupTrace.IsComplete) - { - await action(); - return; - } - - await _startupTrace.MeasureAsync(phase, action); - } - private void PopulateMenuSubmenus() { PopulateRadioSubMenu(TemperatureUnitMenu, [ @@ -547,7 +506,7 @@ public sealed partial class MainWindow : Window private void RebuildSensorTree() { - MeasureStartup("MainWindow.RebuildSensorTree", RebuildSensorTreeCore, GetRootSizeDetail); + _startupTrace.Measure("MainWindow.RebuildSensorTree", RebuildSensorTreeCore, GetRootSizeDetail); _columnMeasurer.ScheduleSettle(); SyncTraySensors(); SyncGadgetSensors(); @@ -842,7 +801,7 @@ public sealed partial class MainWindow : Window private void UpdateSensorColumnWidths() { - MeasureStartup("MainWindow.UpdateSensorColumnWidths", _columnMeasurer.UpdateWidths, () => FormattableString.Invariant($"rows={_columnMeasurer.RowCount}, cacheEntries={_columnMeasurer.CacheEntryCount}, deviceWidth={_columnMeasurer.DeviceColumnWidth:F0}, settled={_columnMeasurer.IsSettled}")); + _startupTrace.Measure("MainWindow.UpdateSensorColumnWidths", _columnMeasurer.UpdateWidths, () => FormattableString.Invariant($"rows={_columnMeasurer.RowCount}, cacheEntries={_columnMeasurer.CacheEntryCount}, deviceWidth={_columnMeasurer.DeviceColumnWidth:F0}, settled={_columnMeasurer.IsSettled}")); } private TextBlock CreateHeaderText(string text, int column, Visibility visibility, string? bindingPath = null) diff --git a/LibreHardwareMonitor.Windows.WinUI/Services/HardwareMonitorService.cs b/LibreHardwareMonitor.Windows.WinUI/Services/HardwareMonitorService.cs index 739d4c3..942c26a 100644 --- a/LibreHardwareMonitor.Windows.WinUI/Services/HardwareMonitorService.cs +++ b/LibreHardwareMonitor.Windows.WinUI/Services/HardwareMonitorService.cs @@ -228,34 +228,26 @@ public sealed class HardwareMonitorService : IHardwareMonitorService ForceDriveWakeup = Settings.GetValue("forceDriveWakeupItem", false); } + private static readonly string[] WinUiDeferredDetectionDefaults = + { + DeferDimmDetectionSetting, + DeferCpuInitialUpdateSetting, + DeferTscEstimationSetting, + DeferNvidiaDetectionSetting, + DeferStorageDetectionSetting, + DeferNetworkDetectionSetting, + DeferIntelGpuDetectionSetting, + DeferControllerDetectionSetting, + DeferPsuDetectionSetting, + }; + private void ApplyWinUiHardwareDefaults() { - if (!Settings.Contains(DeferDimmDetectionSetting)) - Settings.SetValue(DeferDimmDetectionSetting, true); - - if (!Settings.Contains(DeferCpuInitialUpdateSetting)) - Settings.SetValue(DeferCpuInitialUpdateSetting, true); - - if (!Settings.Contains(DeferTscEstimationSetting)) - Settings.SetValue(DeferTscEstimationSetting, true); - - if (!Settings.Contains(DeferNvidiaDetectionSetting)) - Settings.SetValue(DeferNvidiaDetectionSetting, true); - - if (!Settings.Contains(DeferStorageDetectionSetting)) - Settings.SetValue(DeferStorageDetectionSetting, true); - - if (!Settings.Contains(DeferNetworkDetectionSetting)) - Settings.SetValue(DeferNetworkDetectionSetting, true); - - if (!Settings.Contains(DeferIntelGpuDetectionSetting)) - Settings.SetValue(DeferIntelGpuDetectionSetting, true); - - if (!Settings.Contains(DeferControllerDetectionSetting)) - Settings.SetValue(DeferControllerDetectionSetting, true); - - if (!Settings.Contains(DeferPsuDetectionSetting)) - Settings.SetValue(DeferPsuDetectionSetting, true); + foreach (string setting in WinUiDeferredDetectionDefaults) + { + if (!Settings.Contains(setting)) + Settings.SetValue(setting, true); + } } private void SetHardwareEnabled(string settingName, bool value, Action setter) diff --git a/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/FileStartupTracer.cs b/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/FileStartupTracer.cs index 2846f7f..4fc98ca 100644 --- a/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/FileStartupTracer.cs +++ b/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/FileStartupTracer.cs @@ -99,6 +99,12 @@ internal sealed class FileStartupTracer : IStartupTracer public void Measure(string phase, Action action, Func? getDetail) { + if (IsComplete) + { + action(); + return; + } + TimeSpan start = _totalStopwatch.Elapsed; Stopwatch stopwatch = Stopwatch.StartNew(); try @@ -117,18 +123,16 @@ internal sealed class FileStartupTracer : IStartupTracer public T Measure(string phase, Func action) { - return Measure(phase, action, null); - } + if (IsComplete) + return action(); - public T Measure(string phase, Func action, Func? getDetail) - { TimeSpan start = _totalStopwatch.Elapsed; Stopwatch stopwatch = Stopwatch.StartNew(); try { T result = action(); stopwatch.Stop(); - AddEntry(phase, start, stopwatch.Elapsed, "OK", GetDetail(result, getDetail)); + AddEntry(phase, start, stopwatch.Elapsed, "OK", ""); return result; } catch (Exception ex) @@ -141,42 +145,19 @@ internal sealed class FileStartupTracer : IStartupTracer public async Task MeasureAsync(string phase, Func action) { - await MeasureAsync(phase, action, null); - } + if (IsComplete) + { + await action(); + return; + } - public async Task MeasureAsync(string phase, Func action, Func? getDetail) - { TimeSpan start = _totalStopwatch.Elapsed; Stopwatch stopwatch = Stopwatch.StartNew(); try { await action(); stopwatch.Stop(); - AddEntry(phase, start, stopwatch.Elapsed, "OK", GetDetail(getDetail)); - } - catch (Exception ex) - { - stopwatch.Stop(); - AddEntry(phase, start, stopwatch.Elapsed, "ERROR", $"{ex.GetType().FullName}: {ex.Message}"); - throw; - } - } - - public async Task MeasureAsync(string phase, Func> action) - { - return await MeasureAsync(phase, action, null); - } - - public async Task MeasureAsync(string phase, Func> action, Func? getDetail) - { - TimeSpan start = _totalStopwatch.Elapsed; - Stopwatch stopwatch = Stopwatch.StartNew(); - try - { - T result = await action(); - stopwatch.Stop(); - AddEntry(phase, start, stopwatch.Elapsed, "OK", GetDetail(result, getDetail)); - return result; + AddEntry(phase, start, stopwatch.Elapsed, "OK", ""); } catch (Exception ex) { @@ -212,21 +193,6 @@ internal sealed class FileStartupTracer : IStartupTracer } } - private static string GetDetail(T result, Func? getDetail) - { - if (getDetail == null) - return ""; - - try - { - return getDetail(result) ?? ""; - } - catch (Exception ex) - { - return $"Detail unavailable: {ex.GetType().FullName}: {ex.Message}"; - } - } - private void AddEntry(string phase, TimeSpan elapsed, string status, string detail) { AddEntry(phase, _totalStopwatch.Elapsed, elapsed, status, detail); diff --git a/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/IStartupTracer.cs b/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/IStartupTracer.cs index 2938642..a8d01d4 100644 --- a/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/IStartupTracer.cs +++ b/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/IStartupTracer.cs @@ -27,13 +27,5 @@ internal interface IStartupTracer : IDisposable T Measure(string phase, Func action); - T Measure(string phase, Func action, Func? getDetail); - Task MeasureAsync(string phase, Func action); - - Task MeasureAsync(string phase, Func action, Func? getDetail); - - Task MeasureAsync(string phase, Func> action); - - Task MeasureAsync(string phase, Func> action, Func? getDetail); } diff --git a/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/NoOpStartupTracer.cs b/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/NoOpStartupTracer.cs index 83b90f3..92723ac 100644 --- a/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/NoOpStartupTracer.cs +++ b/LibreHardwareMonitor.Windows.WinUI/Services/Tracing/NoOpStartupTracer.cs @@ -48,31 +48,11 @@ internal sealed class NoOpStartupTracer : IStartupTracer return action(); } - public T Measure(string phase, Func action, Func? getDetail) - { - return action(); - } - public Task MeasureAsync(string phase, Func action) { return action(); } - public Task MeasureAsync(string phase, Func action, Func? getDetail) - { - return action(); - } - - public Task MeasureAsync(string phase, Func> action) - { - return action(); - } - - public Task MeasureAsync(string phase, Func> action, Func? getDetail) - { - return action(); - } - public void Dispose() { } diff --git a/LibreHardwareMonitor.Windows.WinUI/ViewModels/MainWindowViewModel.cs b/LibreHardwareMonitor.Windows.WinUI/ViewModels/MainWindowViewModel.cs index 7d92acc..eaff9e0 100644 --- a/LibreHardwareMonitor.Windows.WinUI/ViewModels/MainWindowViewModel.cs +++ b/LibreHardwareMonitor.Windows.WinUI/ViewModels/MainWindowViewModel.cs @@ -873,12 +873,12 @@ public sealed class MainWindowViewModel : ViewModelBase, IDisposable IsHardwareLoading = true; _startupTrace.Mark("MainWindowViewModel.StartAsync.Begin"); StatusText = "Initializing hardware sensors..."; - await MeasureStartupAsync("MainWindowViewModel.HardwareMonitor.OpenAsync", () => _hardwareMonitor.OpenAsync(raiseTreeRebuilt: false)); - MeasureStartup("MainWindowViewModel.UpdateRoot", UpdateRoot, GetRootDetail); + await _startupTrace.MeasureAsync("MainWindowViewModel.HardwareMonitor.OpenAsync", () => _hardwareMonitor.OpenAsync(raiseTreeRebuilt: false)); + _startupTrace.Measure("MainWindowViewModel.UpdateRoot", UpdateRoot, GetRootDetail); StatusText = "Reading sensor values..."; - await MeasureStartupAsync("MainWindowViewModel.InitialSensorValueUpdate", () => RefreshSensorValuesAsync(trackPlotPoints: true, logSensors: false)); - MeasureStartup("MainWindowViewModel.StartWebServerFromSettings", StartWebServerFromSettings); - MeasureStartup("MainWindowViewModel.UpdateStatus", UpdateStatus, GetRootDetail); + await _startupTrace.MeasureAsync("MainWindowViewModel.InitialSensorValueUpdate", () => RefreshSensorValuesAsync(trackPlotPoints: true, logSensors: false)); + _startupTrace.Measure("MainWindowViewModel.StartWebServerFromSettings", StartWebServerFromSettings); + _startupTrace.Measure("MainWindowViewModel.UpdateStatus", UpdateStatus, GetRootDetail); _isStarted = true; _startupTrace.Mark("MainWindowViewModel.StartAsync.Complete", GetRootDetail()); } @@ -956,47 +956,6 @@ public sealed class MainWindowViewModel : ViewModelBase, IDisposable return $"{hardwareCount} hardware device(s), {sensorCount} sensor(s)"; } - private void MeasureStartup(string phase, Action action) - { - if (_startupTrace.IsComplete) - { - action(); - return; - } - - _startupTrace.Measure(phase, action); - } - - private void MeasureStartup(string phase, Action action, Func getDetail) - { - if (_startupTrace.IsComplete) - { - action(); - return; - } - - _startupTrace.Measure(phase, action, getDetail); - } - - private T MeasureStartup(string phase, Func action) - { - if (_startupTrace.IsComplete) - return action(); - - return _startupTrace.Measure(phase, action); - } - - private async Task MeasureStartupAsync(string phase, Func action) - { - if (_startupTrace.IsComplete) - { - await action(); - return; - } - - await _startupTrace.MeasureAsync(phase, action); - } - private void NotifyColumnVisibilityChanged() { OnPropertyChanged(nameof(ValueColumnVisibility)); @@ -1038,11 +997,11 @@ public sealed class MainWindowViewModel : ViewModelBase, IDisposable private void UpdateRoot() { - MeasureStartup("MainWindowViewModel.RootItems.Clear", RootItems.Clear); - SensorTreeItemViewModel root = MeasureStartup("MainWindowViewModel.GetHardwareRoot", () => _hardwareMonitor.Root); - MeasureStartup("MainWindowViewModel.ConfigureRoot", () => root.Configure(TemperatureUnit, ShowHiddenSensors, ShowValueColumn, ShowMinColumn, ShowMaxColumn), GetRootDetail); - MeasureStartup("MainWindowViewModel.RootItems.Add", () => RootItems.Add(root), GetRootDetail); - MeasureStartup("MainWindowViewModel.ApplySensorValuesTimeWindow", ApplySensorValuesTimeWindow, GetRootDetail); + _startupTrace.Measure("MainWindowViewModel.RootItems.Clear", RootItems.Clear); + SensorTreeItemViewModel root = _startupTrace.Measure("MainWindowViewModel.GetHardwareRoot", () => _hardwareMonitor.Root); + _startupTrace.Measure("MainWindowViewModel.ConfigureRoot", () => root.Configure(TemperatureUnit, ShowHiddenSensors, ShowValueColumn, ShowMinColumn, ShowMaxColumn), GetRootDetail); + _startupTrace.Measure("MainWindowViewModel.RootItems.Add", () => RootItems.Add(root), GetRootDetail); + _startupTrace.Measure("MainWindowViewModel.ApplySensorValuesTimeWindow", ApplySensorValuesTimeWindow, GetRootDetail); RefreshStatusCounts(); } diff --git a/LibreHardwareMonitorLib/Hardware/Computer.cs b/LibreHardwareMonitorLib/Hardware/Computer.cs index e544903..e5addc6 100644 --- a/LibreHardwareMonitorLib/Hardware/Computer.cs +++ b/LibreHardwareMonitorLib/Hardware/Computer.cs @@ -29,6 +29,7 @@ using LibreHardwareMonitor.Hardware.PowerMonitor; using LibreHardwareMonitor.Hardware.Psu.Corsair; using LibreHardwareMonitor.Hardware.Psu.Msi; using LibreHardwareMonitor.Hardware.Storage; +using static LibreHardwareMonitor.Hardware.HardwareStartupTrace; namespace LibreHardwareMonitor.Hardware; @@ -769,19 +770,6 @@ public class Computer : IComputer AddMeasuredGroup(startupTrace, "BatteryGroup", () => new BatteryGroup(_settings)); } - private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action) - { - if (startupTrace != null) - startupTrace.Measure(phase, action); - else - action(); - } - - private static T Measure(HardwareStartupTrace startupTrace, string phase, Func action) - { - return startupTrace != null ? startupTrace.Measure(phase, action) : action(); - } - private void AddMeasuredGroup(HardwareStartupTrace startupTrace, string phase, Func createGroup) { Add(Measure(startupTrace, phase, createGroup)); diff --git a/LibreHardwareMonitorLib/Hardware/Cpu/Amd0FCpu.cs b/LibreHardwareMonitorLib/Hardware/Cpu/Amd0FCpu.cs index 5dd7b83..92cc9bc 100644 --- a/LibreHardwareMonitorLib/Hardware/Cpu/Amd0FCpu.cs +++ b/LibreHardwareMonitorLib/Hardware/Cpu/Amd0FCpu.cs @@ -93,7 +93,9 @@ internal sealed class Amd0FCpu : AmdCpu Mutexes.ReleasePciBus(); } - if (HasTimeStampCounter) + // TimeStampCounterFrequency is 0 while deferred TSC estimation is still pending; skip the clock math until it + // lands so the core/bus clocks keep their previous value instead of momentarily reporting 0 MHz (see IntelCpu). + if (HasTimeStampCounter && TimeStampCounterFrequency > 0) { double newBusClock = 0; diff --git a/LibreHardwareMonitorLib/Hardware/Cpu/Amd17Cpu.cs b/LibreHardwareMonitorLib/Hardware/Cpu/Amd17Cpu.cs index 54e6dae..a451aa4 100644 --- a/LibreHardwareMonitorLib/Hardware/Cpu/Amd17Cpu.cs +++ b/LibreHardwareMonitorLib/Hardware/Cpu/Amd17Cpu.cs @@ -396,7 +396,11 @@ internal sealed class Amd17Cpu : AmdCpu } double timeStampCounterMultiplier = GetTimeStampCounterMultiplier(); - if (timeStampCounterMultiplier > 0) + + // When TSC estimation is deferred to the background, TimeStampCounterFrequency is 0 until the estimate + // lands (or GenericCpu.Update self-corrects it from real TSC deltas). Skip the bus-clock math until then so + // the sensor keeps its previous value instead of momentarily reporting 0 MHz (mirrors IntelCpu). + if (timeStampCounterMultiplier > 0 && _cpu.TimeStampCounterFrequency > 0) { _busClock.Value = (float)(_cpu.TimeStampCounterFrequency / timeStampCounterMultiplier); _cpu.ActivateSensor(_busClock); diff --git a/LibreHardwareMonitorLib/Hardware/Cpu/CpuGroup.cs b/LibreHardwareMonitorLib/Hardware/Cpu/CpuGroup.cs index 64f878c..82afa5b 100644 --- a/LibreHardwareMonitorLib/Hardware/Cpu/CpuGroup.cs +++ b/LibreHardwareMonitorLib/Hardware/Cpu/CpuGroup.cs @@ -8,6 +8,7 @@ using System; using System.Collections.Generic; using System.Globalization; using System.Text; +using static LibreHardwareMonitor.Hardware.HardwareStartupTrace; namespace LibreHardwareMonitor.Hardware.Cpu; @@ -41,7 +42,7 @@ internal class CpuGroup : IGroup _threads[index] = coreThreads; GenericCpu cpu = Measure(startupTrace, - $"CpuGroup.Processor{index}.{GetCpuConstructorName(threads[0])}", + $"CpuGroup.Processor{index}.CreateCpu", () => CreateCpu(index, coreThreads, settings, startupTrace), DescribeCpu); if (cpu != null) @@ -137,36 +138,6 @@ internal class CpuGroup : IGroup } } - private static string GetCpuConstructorName(CpuId thread) - { - switch (thread.Vendor) - { - case Vendor.Intel: - return nameof(IntelCpu); - case Vendor.AMD: - switch (thread.Family) - { - case 0x0F: - return nameof(Amd0FCpu); - case 0x10: - case 0x11: - case 0x12: - case 0x14: - case 0x15: - case 0x16: - return "UnsupportedAmdCpu"; - case 0x17: - case 0x19: - case 0x1A: - return nameof(Amd17Cpu); - default: - return nameof(GenericCpu); - } - default: - return nameof(GenericCpu); - } - } - private static string DescribeProcessorThreads(CpuId[][] processorThreads) { int threadCount = 0; @@ -178,12 +149,7 @@ internal class CpuGroup : IGroup private static string DescribeCpu(GenericCpu cpu) { - return cpu != null ? $"{cpu.Name}, {cpu.Sensors.Length} sensor(s)" : "Unsupported CPU family"; - } - - private static T Measure(HardwareStartupTrace startupTrace, string phase, Func action, Func getDetail) - { - return startupTrace != null ? startupTrace.Measure(phase, action, getDetail) : action(); + return cpu != null ? $"{cpu.GetType().Name}: {cpu.Name}, {cpu.Sensors.Length} sensor(s)" : "Unsupported CPU family"; } private static CpuId[][] GetProcessorThreads() diff --git a/LibreHardwareMonitorLib/Hardware/Cpu/GenericCpu.cs b/LibreHardwareMonitorLib/Hardware/Cpu/GenericCpu.cs index 64b4e90..0f09b05 100644 --- a/LibreHardwareMonitorLib/Hardware/Cpu/GenericCpu.cs +++ b/LibreHardwareMonitorLib/Hardware/Cpu/GenericCpu.cs @@ -11,6 +11,7 @@ using System.Linq; using System.Text; using System.Threading; using System.Threading.Tasks; +using static LibreHardwareMonitor.Hardware.HardwareStartupTrace; namespace LibreHardwareMonitor.Hardware.Cpu; @@ -182,19 +183,6 @@ public class GenericCpu : Hardware return new Identifier(s, processorIndex.ToString(CultureInfo.InvariantCulture)); } - private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action) - { - if (startupTrace != null) - startupTrace.Measure(phase, action); - else - action(); - } - - private static T Measure(HardwareStartupTrace startupTrace, string phase, Func action, Func getDetail) - { - return startupTrace != null ? startupTrace.Measure(phase, action, getDetail) : action(); - } - private static void EstimateTimeStampCounterFrequency(CancellationToken cancellationToken, out double frequency, out double error) { // preload the function diff --git a/LibreHardwareMonitorLib/Hardware/Cpu/IntelCpu.cs b/LibreHardwareMonitorLib/Hardware/Cpu/IntelCpu.cs index a446755..4b095a8 100644 --- a/LibreHardwareMonitorLib/Hardware/Cpu/IntelCpu.cs +++ b/LibreHardwareMonitorLib/Hardware/Cpu/IntelCpu.cs @@ -9,6 +9,7 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Text; using LibreHardwareMonitor.PawnIo; +using static LibreHardwareMonitor.Hardware.HardwareStartupTrace; namespace LibreHardwareMonitor.Hardware.Cpu; @@ -572,19 +573,6 @@ internal sealed class IntelCpu : GenericCpu return result; } - private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action) - { - if (startupTrace != null) - startupTrace.Measure(phase, action); - else - action(); - } - - private static T Measure(HardwareStartupTrace startupTrace, string phase, Func action) - { - return startupTrace != null ? startupTrace.Measure(phase, action) : action(); - } - private static bool ShouldDeferInitialUpdate(ISettings settings) { return SettingsParsing.ShouldDefer(settings, DeferInitialUpdateSetting, DeferInitialUpdateEnvironmentVariable); diff --git a/LibreHardwareMonitorLib/Hardware/HardwareStartupTrace.cs b/LibreHardwareMonitorLib/Hardware/HardwareStartupTrace.cs index c5ce7f9..af65399 100644 --- a/LibreHardwareMonitorLib/Hardware/HardwareStartupTrace.cs +++ b/LibreHardwareMonitorLib/Hardware/HardwareStartupTrace.cs @@ -92,6 +92,30 @@ internal sealed class HardwareStartupTrace : IDisposable AddEntry(phase, TimeSpan.Zero, "SKIPPED", null, reason); } + /// + /// Runs under , or directly when tracing is disabled + /// ( is null). Lets callers measure optional startup phases without repeating a null check. + /// + public static void Measure(HardwareStartupTrace trace, string phase, Action action) + { + if (trace != null) + trace.Measure(phase, action); + else + action(); + } + + /// + public static T Measure(HardwareStartupTrace trace, string phase, Func action) + { + return trace != null ? trace.Measure(phase, action) : action(); + } + + /// + public static T Measure(HardwareStartupTrace trace, string phase, Func action, Func getDetail) + { + return trace != null ? trace.Measure(phase, action, getDetail) : action(); + } + private static bool IsEnabled(ISettings settings) { string settingValue = settings.GetValue(EnabledSetting, "false"); diff --git a/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs b/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs index 6b38550..2faf2be 100644 --- a/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs +++ b/LibreHardwareMonitorLib/Hardware/Memory/MemoryGroup.cs @@ -16,6 +16,7 @@ using RAMSPDToolkit.SPD; using RAMSPDToolkit.SPD.Enums; using RAMSPDToolkit.SPD.Interop.Shared; using RAMSPDToolkit.Windows.Driver; +using static LibreHardwareMonitor.Hardware.HardwareStartupTrace; namespace LibreHardwareMonitor.Hardware.Memory; @@ -273,22 +274,4 @@ internal class MemoryGroup : IGroup, IHardwareChanged, IHardwareDiscoveryTask foreach (Hardware hardware in additions) HardwareAdded?.Invoke(hardware); } - - private static void Measure(HardwareStartupTrace startupTrace, string phase, Action action) - { - if (startupTrace != null) - startupTrace.Measure(phase, action); - else - action(); - } - - private static T Measure(HardwareStartupTrace startupTrace, string phase, Func action) - { - return startupTrace != null ? startupTrace.Measure(phase, action) : action(); - } - - private static T Measure(HardwareStartupTrace startupTrace, string phase, Func action, Func getDetail) - { - return startupTrace != null ? startupTrace.Measure(phase, action, getDetail) : action(); - } } diff --git a/docs/code-review-followup.md b/docs/code-review-followup.md index 356488c..7c0b3a2 100644 --- a/docs/code-review-followup.md +++ b/docs/code-review-followup.md @@ -91,7 +91,7 @@ Removed from `AddDeferredGroup`/`AddDeferredGroups` (unreachable since `StartDef - **`CpuGroup`** was **not** parallelized — only refactored to extract `CreateCpu`. Returning `null` for unsupported AMD families preserves the original "add nothing" behavior. - **`TreeRebuildCoalescer`** correctly coalesces a burst into ~one rebuild (single debounced worker, re-arms on a late change). - **Disposed-`CancellationToken` reads** after cancellation are safe (`IsCancellationRequested` does not throw post-dispose). -- **Startup tracing** is fully no-op when disabled (`HardwareStartupTrace.Create` returns `null`; the `Measure` wrappers and `MainWindowViewModel.MeasureStartup` short-circuit). +- **Startup tracing** is fully no-op when disabled (`HardwareStartupTrace.Create` returns `null` and the static `HardwareStartupTrace.Measure` helpers run the action directly; on the WinUI side `NoOpStartupTracer` does the same, and `FileStartupTracer` short-circuits once startup is complete). - **WinUI tree updates are marshaled**: the VM's `HardwareMonitor_TreeRebuilt` handler marshals to the UI via `DispatcherQueue.TryEnqueue`. (Only `Logger` was unmarshaled — fixed in F3.) ---