Browse Source

Added more info to crash report for linux

flabbet 6 months ago
parent
commit
a33315273c

+ 17 - 0
src/PixiEditor.Linux/LinuxOperatingSystem.cs

@@ -86,4 +86,21 @@ public sealed class LinuxOperatingSystem : IOperatingSystem
 
         public override string ToString() => $"{Name} {Version}";
     }
+
+    public string GetActiveDesktopEnvironment()
+    {
+        var desktopSession = Environment.GetEnvironmentVariable("DESKTOP_SESSION");
+        if (desktopSession != null)
+        {
+            return desktopSession;
+        }
+
+        var desktopSessionFile = Environment.GetEnvironmentVariable("XDG_CURRENT_DESKTOP");
+        if (desktopSessionFile != null)
+        {
+            return desktopSessionFile;
+        }
+
+        return "Unknown";
+    }
 }

+ 76 - 43
src/PixiEditor/Models/ExceptionHandling/CrashReport.cs

@@ -1,17 +1,16 @@
-using System.Collections.Generic;
-using System.Diagnostics;
+using System.Diagnostics;
 using System.Globalization;
-using System.IO;
 using System.IO.Compression;
-using System.Linq;
 using System.Reflection;
 using System.Text;
 using Newtonsoft.Json;
-using PixiEditor.Models.Preferences;
 using PixiEditor.Extensions.Common.Localization;
 using PixiEditor.Extensions.CommonApi.UserPreferences;
 using PixiEditor.Extensions.CommonApi.UserPreferences.Settings.PixiEditor;
 using PixiEditor.Helpers;
+#if LINUX
+using PixiEditor.Linux;
+#endif
 using PixiEditor.Models.AnalyticsAPI;
 using PixiEditor.Models.Commands;
 using PixiEditor.OperatingSystem;
@@ -34,7 +33,7 @@ internal class CrashReport : IDisposable
 
         apiReport.Version = VersionHelpers.GetCurrentAssemblyVersion();
         apiReport.BuildId = VersionHelpers.GetBuildId();
-        
+
         apiReport.ReportTime = currentTime.UtcDateTime;
         apiReport.ProcessStart = processStartTime.ToUniversalTime();
         apiReport.IsCrash = nonCrashInfo == null;
@@ -51,6 +50,13 @@ internal class CrashReport : IDisposable
 
             apiReport.SystemInformation["PlatformId"] = os.AnalyticsId;
             apiReport.SystemInformation["PlatformName"] = os.AnalyticsName;
+            apiReport.SystemInformation["OSVersion"] = Environment.OSVersion.VersionString;
+#if LINUX
+            if (os is LinuxOperatingSystem linux)
+            {
+                apiReport.SystemInformation["DesktopEnvironment"] = linux.GetActiveDesktopEnvironment();
+            }
+#endif
         }
         catch (Exception e)
         {
@@ -62,7 +68,7 @@ internal class CrashReport : IDisposable
             var sessionId = AnalyticsPeriodicReporter.Instance?.SessionId;
 
             if (sessionId == Guid.Empty) sessionId = null;
-            
+
             apiReport.SessionId = sessionId;
         }
         catch (Exception e)
@@ -77,10 +83,13 @@ internal class CrashReport : IDisposable
                 $"Application started {GetFormatted(() => processStartTime, "yyyy.MM.dd HH:hh:ss")}, {GetFormatted(() => currentTime - processStartTime, @"d\ hh\:mm\.ss")} ago")
             .AppendLine($"Report: {Guid.NewGuid()}\n")
             .AppendLine("-----System Information----")
-            .AppendLine("General:")
-            .AppendLine($"  OS: {IOperatingSystem.Current.AnalyticsName}")
-            .AppendLine($"  OS Version: {Environment.OSVersion.VersionString}")
-            .AppendLine();
+            .AppendLine("General:");
+        foreach (var sysInfo in apiReport.SystemInformation)
+        {
+            builder.AppendLine($"  {sysInfo.Key}: {sysInfo.Value}");
+        }
+
+        builder.AppendLine();
 
         CrashHelper helper = new();
 
@@ -94,7 +103,8 @@ internal class CrashReport : IDisposable
         }
         catch (Exception cemLogException)
         {
-            builder.AppendLine($"Error ({cemLogException.GetType().FullName}: {cemLogException.Message}) while gathering command log, skipping...");
+            builder.AppendLine(
+                $"Error ({cemLogException.GetType().FullName}: {cemLogException.Message}) while gathering command log, skipping...");
         }
 
         builder.AppendLine("\n-----------State-----------");
@@ -105,8 +115,10 @@ internal class CrashReport : IDisposable
         }
         catch (Exception stateException)
         {
-            exception = new AggregateException(exception, new CrashInfoCollectionException("state information", stateException));
-            builder.AppendLine($"Error ({stateException.GetType().FullName}: {stateException.Message}) while gathering state (Must be bug in GetPreferenceFormatted, GetFormatted or StringBuilder.AppendLine as these should not throw), skipping...");
+            exception = new AggregateException(exception,
+                new CrashInfoCollectionException("state information", stateException));
+            builder.AppendLine(
+                $"Error ({stateException.GetType().FullName}: {stateException.Message}) while gathering state (Must be bug in GetPreferenceFormatted, GetFormatted or StringBuilder.AppendLine as these should not throw), skipping...");
         }
 
         apiReport.Exception = new ExceptionDetails(exception);
@@ -145,7 +157,8 @@ internal class CrashReport : IDisposable
         }
         catch (Exception cpuE)
         {
-            builder.AppendLine($"Error ({cpuE.GetType().FullName}: {cpuE.Message}) while gathering CPU information, skipping...");
+            builder.AppendLine(
+                $"Error ({cpuE.GetType().FullName}: {cpuE.Message}) while gathering CPU information, skipping...");
         }
 
         try
@@ -154,19 +167,21 @@ internal class CrashReport : IDisposable
         }
         catch (Exception gpuE)
         {
-            builder.AppendLine($"Error ({gpuE.GetType().FullName}: {gpuE.Message}) while gathering GPU information, skipping...");
+            builder.AppendLine(
+                $"Error ({gpuE.GetType().FullName}: {gpuE.Message}) while gathering GPU information, skipping...");
         }
 
-        
+
         try
         {
             helper.GetMemoryInformation(builder, apiReport);
         }
         catch (Exception memE)
         {
-            builder.AppendLine($"Error ({memE.GetType().FullName}: {memE.Message}) while gathering memory information, skipping...");
+            builder.AppendLine(
+                $"Error ({memE.GetType().FullName}: {memE.Message}) while gathering memory information, skipping...");
         }
-}
+    }
 
     private static void AppendStateInfo(StringBuilder builder, ApiCrashReport apiReport)
     {
@@ -189,17 +204,20 @@ internal class CrashReport : IDisposable
             .AppendLine($"  MainWindow Size: {GetFormatted(() => MainWindow.Current?.Bounds)}")
             .AppendLine($"  MainWindow State: {GetFormatted(() => MainWindow.Current?.WindowState)}")
             .AppendLine("\nViewModels:")
-            .AppendLine($"  Has active updateable change: {GetFormatted(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.BlockingUpdateableChangeActive)}")
-            .AppendLine($"  Current Tool: {GetFormattedFromViewModelMain(x => x.ToolsSubViewModel?.ActiveTool?.ToolName)}")
+            .AppendLine(
+                $"  Has active updateable change: {GetFormatted(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.BlockingUpdateableChangeActive)}")
+            .AppendLine(
+                $"  Current Tool: {GetFormattedFromViewModelMain(x => x.ToolsSubViewModel?.ActiveTool?.ToolName)}")
             .AppendLine($"  Primary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.PrimaryColor)}")
-            .AppendLine($"  Secondary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.SecondaryColor)}")
+            .AppendLine(
+                $"  Secondary Color: {GetFormattedFromViewModelMain(x => x.ColorsSubViewModel?.SecondaryColor)}")
             .Append("\nActive Document: ");
 
         apiReport.StateInformation["Environment"] = new
         {
             ThreadCount = GetOrExceptionMessage(() => Process.GetCurrentProcess().Threads.Count)
         };
-        
+
         apiReport.StateInformation["Culture"] = new
         {
             SelectedLanguage = GetPreferenceFormatted("LanguageCode", true, "system"),
@@ -224,10 +242,16 @@ internal class CrashReport : IDisposable
 
         apiReport.StateInformation["ViewModels"] = new
         {
-            HasActiveUpdateableChange = GetOrExceptionMessage(() => ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument?.BlockingUpdateableChangeActive),
-            CurrentTool = GetOrExceptionMessage(() => ViewModelMain.Current?.ToolsSubViewModel?.ActiveTool?.ToolName),
-            PrimaryColor = GetOrExceptionMessage(() => ViewModelMain.Current?.ColorsSubViewModel?.PrimaryColor.ToString()),
-            SecondaryColor = GetOrExceptionMessage(() => ViewModelMain.Current?.ColorsSubViewModel?.SecondaryColor.ToString())
+            HasActiveUpdateableChange =
+                GetOrExceptionMessage(() =>
+                    ViewModelMain.Current?.DocumentManagerSubViewModel?.ActiveDocument
+                        ?.BlockingUpdateableChangeActive),
+            CurrentTool =
+                GetOrExceptionMessage(() => ViewModelMain.Current?.ToolsSubViewModel?.ActiveTool?.ToolName),
+            PrimaryColor =
+                GetOrExceptionMessage(() => ViewModelMain.Current?.ColorsSubViewModel?.PrimaryColor.ToString()),
+            SecondaryColor = GetOrExceptionMessage(() =>
+                ViewModelMain.Current?.ColorsSubViewModel?.SecondaryColor.ToString())
         };
 
         apiReport.StateInformation["ActiveDocument"] = new { };
@@ -288,9 +312,9 @@ internal class CrashReport : IDisposable
             .AppendLine($"  Updateable Change Active: {FormatObject(document.BlockingUpdateableChangeActive)}")
             .AppendLine($"  Transform: {FormatObject(document.TransformViewModel)}");
     }
-    
+
     private static object GetPreferenceOrExceptionMessage<T>(string name, bool roaming, T defaultValue)
-    {        
+    {
         try
         {
             var preferences = IPreferences.Current;
@@ -310,7 +334,8 @@ internal class CrashReport : IDisposable
         }
     }
 
-    private static string GetPreferenceFormatted<T>(string name, bool roaming, T defaultValue = default, string? format = null)
+    private static string GetPreferenceFormatted<T>(string name, bool roaming, T defaultValue = default,
+        string? format = null)
     {
         try
         {
@@ -381,11 +406,11 @@ internal class CrashReport : IDisposable
         string FormatLocalizedString(LocalizedString localizedS)
         {
             return localizedS.Parameters != null
-                ? $"{localizedS.Key} @({string.Join(", ", localizedS.Parameters.Select(x => FormatObject(x, format)))})" 
+                ? $"{localizedS.Key} @({string.Join(", ", localizedS.Parameters.Select(x => FormatObject(x, format)))})"
                 : localizedS.Key;
         }
     }
-    
+
     public static CrashReport Parse(string path)
     {
         CrashReport report = new();
@@ -401,7 +426,7 @@ internal class CrashReport : IDisposable
     public string FilePath { get; set; }
 
     public string ReportText { get; set; }
-    
+
     public string ApiReportJson { get; set; }
 
     private ZipArchive ZipFile { get; set; }
@@ -434,15 +459,16 @@ internal class CrashReport : IDisposable
         {
             recoveredDocuments.AddRange(
                 ZipFile.Entries
-                    .Where(x => 
-                        x.FullName.StartsWith("Documents") && 
+                    .Where(x =>
+                        x.FullName.StartsWith("Documents") &&
                         x.FullName.EndsWith(".pixi"))
                     .Select(entry => new RecoveredPixi(null, entry)));
 
             return recoveredDocuments;
         }
 
-        recoveredDocuments.AddRange(sessionInfo.OpenedDocuments.Select(path => new RecoveredPixi(path.OriginalPath, ZipFile.GetEntry($"Documents/{path.ZipName}"))));
+        recoveredDocuments.AddRange(sessionInfo.OpenedDocuments.Select(path =>
+            new RecoveredPixi(path.OriginalPath, ZipFile.GetEntry($"Documents/{path.ZipName}"))));
 
         return recoveredDocuments;
 
@@ -478,8 +504,9 @@ internal class CrashReport : IDisposable
         string fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) +
                           IOperatingSystem.Current.ExecutableExtension;
 #if DEBUG
-        if(!File.Exists(fileName))
-            fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) + $".Desktop{IOperatingSystem.Current.ExecutableExtension}";
+        if (!File.Exists(fileName))
+            fileName = Path.GetFileNameWithoutExtension(Assembly.GetExecutingAssembly().Location) +
+                       $".Desktop{IOperatingSystem.Current.ExecutableExtension}";
 #endif
 
         IOperatingSystem.Current.ProcessUtility.ShellExecute(fileName, $"--crash \"{Path.GetFullPath(FilePath)}\"");
@@ -521,10 +548,13 @@ internal class CrashReport : IDisposable
         {
             try
             {
-                string fileName = string.IsNullOrWhiteSpace(document.FullFilePath) ? "Unsaved" : Path.GetFileNameWithoutExtension(document.FullFilePath);
-                string nameInZip = $"{fileName}-{document.OpenedUTC.ToString(CultureInfo.InvariantCulture)}-{counter.ToString(CultureInfo.InvariantCulture)}.pixi"
-                    .Replace(':', '_')
-                    .Replace('/', '_');
+                string fileName = string.IsNullOrWhiteSpace(document.FullFilePath)
+                    ? "Unsaved"
+                    : Path.GetFileNameWithoutExtension(document.FullFilePath);
+                string nameInZip =
+                    $"{fileName}-{document.OpenedUTC.ToString(CultureInfo.InvariantCulture)}-{counter.ToString(CultureInfo.InvariantCulture)}.pixi"
+                        .Replace(':', '_')
+                        .Replace('/', '_');
 
                 byte[] serialized = PixiParser.V5.Serialize(document.ToSerializable());
 
@@ -534,6 +564,7 @@ internal class CrashReport : IDisposable
                 originalPaths.Add(new CrashedFileInfo(nameInZip, document.FullFilePath));
             }
             catch { }
+
             counter++;
         }
 
@@ -542,7 +573,9 @@ internal class CrashReport : IDisposable
             using Stream jsonStream = archive.CreateEntry("DocumentInfo.json").Open();
             using StreamWriter writer = new StreamWriter(jsonStream);
 
-            string originalPathsJson = JsonConvert.SerializeObject(new CrashedSessionInfo(AnalyticsPeriodicReporter.Instance?.SessionId ?? Guid.Empty, originalPaths), Formatting.Indented);
+            string originalPathsJson = JsonConvert.SerializeObject(
+                new CrashedSessionInfo(AnalyticsPeriodicReporter.Instance?.SessionId ?? Guid.Empty, originalPaths),
+                Formatting.Indented);
             writer.Write(originalPathsJson);
         }
     }