Преглед на файлове

Added deadlock detection debug window

CPKreuz преди 1 година
родител
ревизия
755ff0bbb1

+ 49 - 7
src/PixiEditor/Helpers/DeadlockDetectionHelper.cs

@@ -13,13 +13,43 @@ using ThreadState = System.Threading.ThreadState;
 
 namespace PixiEditor.Helpers;
 
-public class DeadlockDetectionHelper
+internal class DeadlockDetectionHelper
 {
     private Dispatcher dispatcher;
     private Thread mainThread;
     private int checkTimes;
     private int errorsReported;
     private bool shuttingDown;
+
+    private int totalChecks;
+    private int secondStageChecks;
+    private int thirdStageChecks;
+    private int fourthStageChecks;
+    private int deadlocksDetected;
+    
+    public DateTime StartTime { get; private set; }
+    
+    public static DeadlockDetectionHelper Current { get; private set; }
+
+    public int TotalChecks => totalChecks;
+    
+    public int SecondStageChecks => secondStageChecks;
+
+    public int ThirdStageChecks => thirdStageChecks;
+
+    public int FourthStageChecks => fourthStageChecks;
+
+    public int DeadlocksDetected => deadlocksDetected;
+    
+    public DeadlockDetectionHelper()
+    {
+        if (Current != null)
+        {
+            throw new InvalidOperationException("There's already a deadlock detection helper");
+        }
+        
+        Current = this;
+    }
     
     public void Start()
     {
@@ -38,6 +68,8 @@ public class DeadlockDetectionHelper
 
     private void ThreadStart()
     {
+        StartTime = DateTime.Now;
+        
         while (true)
         {
             if (shuttingDown)
@@ -66,6 +98,8 @@ public class DeadlockDetectionHelper
             return;
         }
 
+        Interlocked.Increment(ref deadlocksDetected);
+
         if (errorsReported < 5)
         {
             TryReportProblem();
@@ -184,35 +218,43 @@ public class DeadlockDetectionHelper
         // First deadlock check
         bool isFree = CheckDispatcher(1000, stopwatch, DispatcherPriority.Background);
 
+        Interlocked.Increment(ref totalChecks);
+
         if (isFree)
             return true;
+            
+        stopwatch.Log($"----- First deadlock check time [0] {{0}}; Dispatcher had time: false");
         
-        stopwatch.Log("-------- First deadlock check time [0] {0}");
+        Interlocked.Increment(ref secondStageChecks);
         
         // Second deadlock check
         for (var i = 0; i < 3; i++)
         {
             isFree = CheckDispatcher(400, stopwatch, DispatcherPriority.Input);
 
+            stopwatch.Log($"------ Second deadlock check time [{i}] {{0}}; Dispatcher had time: {isFree}");
+            
             if (isFree)
                 return true;
-            
-            stopwatch.Log($"-------- Second deadlock check time [{i}] {{0}}");
         }
         
+        Interlocked.Increment(ref thirdStageChecks);
+
         // Third deadlock check
         isFree = CheckDispatcher(1600, stopwatch, DispatcherPriority.Input);
 
+        stopwatch.Log($"------- Third deadlock check time [0] {{0}}; Dispatcher had time: {isFree}");
+
         if (isFree)
             return true;
 
-        stopwatch.Log("-------- Third deadlock check time [0] {0}");
-        
+        Interlocked.Increment(ref fourthStageChecks);
+
         // Forth deadlock
         int lastTimeout = mainThread.ThreadState.HasFlag(ThreadState.WaitSleepJoin) ? 1400 : 3500;
         isFree = CheckDispatcher(lastTimeout, stopwatch, DispatcherPriority.Send);
 
-        stopwatch.Log("-------- Fourth deadlock check time [0] {0}");
+        stopwatch.Log($"-------- Fourth deadlock check time [0] {{0}}; Dispatcher had time: {isFree}");
         
         return isFree;
     }

+ 1 - 0
src/PixiEditor/NotifyableObject.cs

@@ -2,6 +2,7 @@
 using System.Runtime.CompilerServices;
 using System.Windows;
 using PixiEditor.Exceptions;
+using PixiEditor.Helpers;
 
 namespace PixiEditor;
 

+ 3 - 0
src/PixiEditor/ViewModels/SubViewModels/Main/DebugViewModel.cs

@@ -210,6 +210,9 @@ internal class DebugViewModel : SubViewModel<ViewModelMain>
         window.Activate();
     }
 
+    [Command.Debug("PixiEditor.Debug.OpenDeadlockDetectionDebugWindow", "Deadlock Detection Debug Popup", "Open Deadlock Detection Debug Popup")]
+    public void OpenDeadlockDetectionDebugPopup() => new DeadlockDetectionDebugPopup().Show();
+
     [Command.Internal("PixiEditor.Debug.SetLanguageFromFilePicker")]
     public void SetLanguageFromFilePicker()
     {

+ 46 - 0
src/PixiEditor/Views/Dialogs/DebugDialogs/DeadlockDetectionDebugPopup.xaml

@@ -0,0 +1,46 @@
+<Window x:Class="PixiEditor.Views.Dialogs.DebugDialogs.DeadlockDetectionDebugPopup"
+        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
+        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
+        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
+        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
+        xmlns:local="clr-namespace:PixiEditor.Views.Dialogs.DebugDialogs"
+        xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
+        xmlns:dialogs="clr-namespace:PixiEditor.Views.Dialogs"
+        xmlns:behaviours="clr-namespace:PixiEditor.Helpers.Behaviours"
+        xmlns:userControls="clr-namespace:PixiEditor.Views.UserControls"
+        mc:Ignorable="d"
+        Foreground="White"
+        Title="DeadlockDetectionDebugPopup" Height="200" Width="400"
+        ResizeMode="NoResize"
+        d:DataContext="{d:DesignInstance local:DeadlockDetectionDebugPopup}">
+
+    <Window.CommandBindings>
+        <CommandBinding Command="{x:Static SystemCommands.CloseWindowCommand}" CanExecute="CommandBinding_CanExecute"
+                        Executed="CommandBinding_Executed_Close" />
+    </Window.CommandBindings>
+
+    <WindowChrome.WindowChrome>
+        <WindowChrome CaptionHeight="32" GlassFrameThickness="0.1"
+                      ResizeBorderThickness="{x:Static SystemParameters.WindowResizeBorderThickness}" />
+    </WindowChrome.WindowChrome>
+
+    <DockPanel Background="{StaticResource AccentColor}" Focusable="True">
+        <b:Interaction.Behaviors>
+            <behaviours:ClearFocusOnClickBehavior />
+        </b:Interaction.Behaviors>
+
+        <dialogs:DialogTitleBar DockPanel.Dock="Top"
+                                CloseCommand="{x:Static SystemCommands.CloseWindowCommand}"
+                                TitleKey="Deadlock Detection Debug" />
+
+        <StackPanel Margin="5">
+            <Button Style="{StaticResource DarkRoundButton}" HorizontalAlignment="Left" Click="Refresh_OnClick">Refresh</Button>
+            <userControls:PrependTextBlock Prepend="Time since start of Deadlock Detection: " Text="{Binding TimeSinceStart, Mode=OneWay}" Margin="0,5"/>
+            <userControls:PrependTextBlock Prepend="Total Checks: " Text="{Binding TotalChecks, Mode=OneWay}"/>
+            <userControls:PrependTextBlock Prepend="Second Stage Checks: " Text="{Binding SecondStageChecks, Mode=OneWay}"/>
+            <userControls:PrependTextBlock Prepend="Third Stage Checks: " Text="{Binding ThirdStageChecks, Mode=OneWay}"/>
+            <userControls:PrependTextBlock Prepend="Fourth Stage Checks: " Text="{Binding FourthStageChecks, Mode=OneWay}"/>
+            <userControls:PrependTextBlock Prepend="Deadlocks Detected: " Text="{Binding DeadlocksDetected, Mode=OneWay}" Margin="0, 5"/>
+        </StackPanel>
+    </DockPanel>
+</Window>

+ 93 - 0
src/PixiEditor/Views/Dialogs/DebugDialogs/DeadlockDetectionDebugPopup.xaml.cs

@@ -0,0 +1,93 @@
+using System.Windows;
+using System.Windows.Input;
+using PixiEditor.Helpers;
+
+namespace PixiEditor.Views.Dialogs.DebugDialogs;
+
+public partial class DeadlockDetectionDebugPopup : Window
+{
+    public static readonly DependencyProperty TimeSinceStartProperty = DependencyProperty.Register(
+        nameof(TimeSinceStart), typeof(string), typeof(DeadlockDetectionDebugPopup), new PropertyMetadata(default(string)));
+
+    public string TimeSinceStart
+    {
+        get { return (string)GetValue(TimeSinceStartProperty); }
+        set { SetValue(TimeSinceStartProperty, value); }
+    }
+    
+    public static readonly DependencyProperty TotalChecksProperty = DependencyProperty.Register(
+        nameof(TotalChecks), typeof(int), typeof(DeadlockDetectionDebugPopup), new PropertyMetadata(default(int)));
+
+    public int TotalChecks
+    {
+        get => (int)GetValue(TotalChecksProperty);
+        set => SetValue(TotalChecksProperty, value);
+    }
+
+    public static readonly DependencyProperty SecondStageChecksProperty = DependencyProperty.Register(
+        nameof(SecondStageChecks), typeof(int), typeof(DeadlockDetectionDebugPopup), new PropertyMetadata(default(int)));
+
+    public int SecondStageChecks
+    {
+        get => (int)GetValue(SecondStageChecksProperty);
+        set => SetValue(SecondStageChecksProperty, value);
+    }
+
+    public static readonly DependencyProperty ThirdStageChecksProperty = DependencyProperty.Register(
+        nameof(ThirdStageChecks), typeof(int), typeof(DeadlockDetectionDebugPopup), new PropertyMetadata(default(int)));
+
+    public int ThirdStageChecks
+    {
+        get => (int)GetValue(ThirdStageChecksProperty);
+        set => SetValue(ThirdStageChecksProperty, value);
+    }
+
+    public static readonly DependencyProperty FourthStageChecksProperty = DependencyProperty.Register(
+        nameof(FourthStageChecks), typeof(int), typeof(DeadlockDetectionDebugPopup), new PropertyMetadata(default(int)));
+
+    public int FourthStageChecks
+    {
+        get => (int)GetValue(FourthStageChecksProperty);
+        set => SetValue(FourthStageChecksProperty, value);
+    }
+
+    public static readonly DependencyProperty DeadlocksDetectedProperty = DependencyProperty.Register(
+        nameof(DeadlocksDetected), typeof(int), typeof(DeadlockDetectionDebugPopup), new PropertyMetadata(default(int)));
+
+    public int DeadlocksDetected
+    {
+        get { return (int)GetValue(DeadlocksDetectedProperty); }
+        set { SetValue(DeadlocksDetectedProperty, value); }
+    }
+
+    private DeadlockDetectionHelper helper;
+    
+    public DeadlockDetectionDebugPopup()
+    {
+        helper = DeadlockDetectionHelper.Current;
+        DataContext = this;
+        InitializeComponent();
+        Refresh_OnClick(null, null);
+    }
+    
+    private void CommandBinding_CanExecute(object sender, CanExecuteRoutedEventArgs e)
+    {
+        e.CanExecute = true;
+    }
+
+    private void CommandBinding_Executed_Close(object sender, ExecutedRoutedEventArgs e)
+    {
+        SystemCommands.CloseWindow(this);
+    }
+
+    private void Refresh_OnClick(object sender, RoutedEventArgs e)
+    {
+        TimeSinceStart = (DateTime.Now - helper.StartTime).ToString();
+        TotalChecks = helper.TotalChecks;
+        SecondStageChecks = helper.SecondStageChecks;
+        ThirdStageChecks = helper.ThirdStageChecks;
+        FourthStageChecks = helper.FourthStageChecks;
+        DeadlocksDetected = helper.DeadlocksDetected;
+    }
+}
+

+ 3 - 0
src/PixiEditor/Views/MainWindow.xaml

@@ -398,6 +398,9 @@
                         <MenuItem
                             ui1:Translator.Key="OPEN_LOCALIZATION_DEBUG_WINDOW"
                             cmds:Menu.Command="PixiEditor.Debug.OpenLocalizationDebugWindow"/>
+                        <MenuItem
+                            Header="Open Deadlock Detection Debug Window"
+                            cmds:Menu.Command="PixiEditor.Debug.OpenDeadlockDetectionDebugWindow"/>
                         <Separator/>
                         <MenuItem
                             ui1:Translator.Key="OPEN_LOCAL_APPDATA_DIR"