Simplify crash report
MODIFY
WalletWasabi.Fluent.Desktop/Program.cs
WalletWasabi.Fluent.Desktop/Program.cs
@@ -31,10 +31,32 @@ namespace WalletWasabi.Fluent.Desktop
// yet and stuff might break.
public static int Main(string[] args)
{
- bool guiStarted = false;
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+ TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
+ bool runGui = true;
+
+ try
+ {
+ if (CrashReporter.TryGetExceptionFromCliArgs(args, out var exceptionToShow))
+ {
+ // Show the exception.
+ Console.WriteLine($"TODO Implement crash reporting. {exceptionToShow}");
+
+ runGui = false;
+ }
+ }
+ catch (Exception ex)
+ {
+ // Anything happens here just log it and do not run the Gui.
+ Logger.LogCritical(ex);
+ runGui = false;
+ }
+
+ Exception? exceptionToReport = null;
SingleInstanceChecker? singleInstanceChecker = null;
- CrashReporter crashReporter = new();
+ if (runGui)
+ {
try
{
string dataDir = EnvironmentHelpers.GetDataDir(Path.Combine("WalletWasabi", "Client"));
@@ -48,16 +70,6 @@ namespace WalletWasabi.Fluent.Desktop
// TODO only required due to statusbar vm... to be removed.
Locator.CurrentMutable.RegisterConstant(Global);
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
- TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException;
-
- if (crashReporter.ProcessCliArgs(args) is { } exceptionToReport)
- {
- // Show the exception
- Console.WriteLine($"TODO Implement crash reporting. {exceptionToReport}");
- }
- else
- {
if (args.Length != 0)
{
ProcessCliCommands(Global, args);
@@ -65,22 +77,23 @@ namespace WalletWasabi.Fluent.Desktop
else
{
Logger.LogSoftwareStarted("Wasabi GUI");
- guiStarted = true;
BuildAvaloniaApp(Global)
.AfterSetup(_ => ThemeHelper.ApplyTheme(Global.UiConfig.DarkModeEnabled))
.StartWithClassicDesktopLifetime(args);
}
}
- }
catch (OperationCanceledException ex)
{
Logger.LogDebug(ex);
}
catch (Exception ex)
{
- crashReporter.SetException(ex);
+ exceptionToReport = ex;
Logger.LogCritical(ex);
}
+ }
+
+ // Start termination/disposal of the application.
TerminateService.Terminate();
@@ -89,18 +102,18 @@ namespace WalletWasabi.Fluent.Desktop
Task.Run(async () => await single.DisposeAsync()).Wait();
}
- AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
- TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException;
-
- if (guiStarted)
+ if (exceptionToReport is { })
{
- // Trigger the CrashReport process.
- crashReporter.TryInvokeIfRequired();
+ // Trigger the CrashReport process if required.
+ CrashReporter.TryInvokeIfRequired(exceptionToReport);
}
+ AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException;
+ TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException;
+
Logger.LogSoftwareStopped("Wasabi");
- return crashReporter.HadException ? 1 : 0;
+ return exceptionToReport is { } ? 1 : 0;
}
private static (UiConfig uiConfig, Config config) LoadOrCreateConfigs(string dataDir)

MODIFY
WalletWasabi.Fluent/CrashReport/CrashReporter.cs
WalletWasabi.Fluent/CrashReport/CrashReporter.cs
@@ -6,38 +6,20 @@ using WalletWasabi.Models;
namespace WalletWasabi.Fluent.CrashReport
{
- public class CrashReporter
+ public static class CrashReporter
{
- private const int MaxRecursiveCalls = 3;
- public int Attempts { get; private set; }
- private string? Base64ExceptionString { get; set; } = null;
-
- /// <summary>
- /// The current Wasabi instance had an exception.
- /// </summary>
- public bool HadException { get; private set; }
-
- public SerializableException? SerializedException { get; private set; }
-
- /// <summary>
- /// Call this right before the end of the application. It will call another Wasabi instance in crash report mode if there is something to report.
- /// The exception can be set by the CLI arguments by calling <see cref="ProcessCliArgs(string[])"/> or <see cref="SetException(Exception)"/>.
- /// </summary>
- public void TryInvokeIfRequired()
+ public static bool TryInvokeIfRequired(Exception exceptionToReport)
{
- if (SerializedException is null)
+ if (exceptionToReport is null)
{
- return;
+ return false;
}
try
{
- if (Attempts >= MaxRecursiveCalls)
- {
- throw new InvalidOperationException($"The crash report has been called {MaxRecursiveCalls} times. Will not continue to avoid recursion errors.");
- }
-
- var args = ToCliArguments();
+ var serializedException = exceptionToReport.ToSerializableException();
+ var base64ExceptionString = SerializableException.ToBase64String(serializedException);
+ var args = $"crashreport -exception=\"{base64ExceptionString}\"";
var path = Process.GetCurrentProcess().MainModule?.FileName;
if (string.IsNullOrEmpty(path))
@@ -47,52 +29,45 @@ namespace WalletWasabi.Fluent.CrashReport
ProcessStartInfo startInfo = ProcessStartInfoFactory.Make(path, args);
using Process? p = Process.Start(startInfo);
+
+ return true;
}
catch (Exception ex)
{
- Logger.LogWarning($"There was a problem while invoking crash report:{ex.ToUserFriendlyString()}.");
- }
- }
-
- public string ToCliArguments()
- {
- if (string.IsNullOrEmpty(Base64ExceptionString))
- {
- throw new InvalidOperationException($"The crash report exception message is empty.");
+ Logger.LogWarning($"There was a problem while invoking crash report: '{ex}'.");
}
- return $"crashreport -attempt=\"{Attempts + 1}\" -exception=\"{Base64ExceptionString}\"";
+ return false;
}
- public SerializableException? ProcessCliArgs(string[] args)
+ public static bool TryGetExceptionFromCliArgs(string[] args, out SerializableException? exception)
{
- if (args.Length < 3)
+ exception = null;
+ try
{
- return null;
+ if (args.Length < 2)
+ {
+ return false;
}
- if (args[0].Contains("crashreport") && args[1].Contains("-attempt=") && args[2].Contains("-exception="))
+ if (args[0].Contains("crashreport") && args[1].Contains("-exception="))
{
- var attemptString = args[1].Split("=", StringSplitOptions.RemoveEmptyEntries)[1].Trim('"');
-
- var exceptionString = args[2].Split("=", StringSplitOptions.RemoveEmptyEntries)[1].Trim('"');
+ var exceptionString = args[1].Split("=", StringSplitOptions.RemoveEmptyEntries)[1].Trim('"');
- Attempts = int.Parse(attemptString);
-
- return SerializableException.FromBase64String(exceptionString);
+ exception = SerializableException.FromBase64String(exceptionString);
+ return true;
+ }
}
+ catch (Exception ex)
+ {
+ // Report the current exception.
+ exception = ex.ToSerializableException();
- return null;
+ Logger.LogCritical($"There was a problem: '{ex}'.");
+ return true;
}
- /// <summary>
- /// Sets the exception when it occurs the first time and should be reported to the user.
- /// </summary>
- public void SetException(Exception ex)
- {
- SerializedException = ex.ToSerializableException();
- Base64ExceptionString = SerializableException.ToBase64String(SerializedException);
- HadException = true;
+ return false;
}
}
}

DELETE
WalletWasabi.Tests/UnitTests/Clients/CrashReportTests.cs
-using System;
-using WalletWasabi.Fluent.CrashReport;
-using Xunit;
-
-namespace WalletWasabi.Tests.UnitTests.Clients
-{
- public class CrashReportTests
- {
- [Fact]
- public void CrashReporterCliArgTest()
- {
- CrashReporter crashing = new();
-
- Exception innerEx = new IndexOutOfRangeException("InnerMessage");
- Exception ex;
- try
- {
- // Throw it to have a stack-trace.
- throw new InvalidOperationException("Message", innerEx);
- }
- catch (Exception exc)
- {
- ex = exc;
- }
-
- crashing.SetException(ex);
- Assert.True(crashing.HadException);
-
- var args = crashing.ToCliArguments();
-
- CrashReporter reporting = new();
-
- // Args are split by space and passed to Main IRL.
- var exceptionFromCli = reporting.ProcessCliArgs(args.Split(' '));
- Assert.Equal(exceptionFromCli, ex.ToSerializableException());
- }
- }
-}

Simplify crash report