mirror of
https://github.com/MaSzyna-EU07/StarterNG.git
synced 2026-11-02 04:39:14 +01:00
Point the crash report at the minidump the simulator left
The simulator writes crash_*.dmp relative to its working directory, and crashpad under crashdumps; the newest one from this run is named, with an offer to open its folder.
This commit is contained in:
@@ -68,6 +68,23 @@ public class StartSimulationTests
|
|||||||
Assert.False(launcher.CapturedErrors);
|
Assert.False(launcher.CapturedErrors);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Minidumps_of_this_run_are_found_in_the_installation_and_under_crashpad()
|
||||||
|
{
|
||||||
|
var (start, _, installation) = Rig(files => files
|
||||||
|
.WithExecutable(Build)
|
||||||
|
.WithFile(TestInstallation.At("crash_2026-10-04_13-03-12.dmp"), "MDMP")
|
||||||
|
.WithFile(TestInstallation.At("crashdumps", "reports", "1a2b.dmp"), "MDMP"));
|
||||||
|
installation.Files.LastWriteTimeUtc = new DateTime(2026, 10, 4, 11, 3, 12, DateTimeKind.Utc);
|
||||||
|
|
||||||
|
var thisRun = start.CrashDumpsSince(new DateTime(2026, 10, 4, 11, 3, 0, DateTimeKind.Utc), Build);
|
||||||
|
var later = start.CrashDumpsSince(new DateTime(2026, 10, 4, 12, 0, 0, DateTimeKind.Utc), Build);
|
||||||
|
|
||||||
|
Assert.Equal(2, thisRun.Count);
|
||||||
|
Assert.Contains(thisRun, dump => dump.EndsWith("crash_2026-10-04_13-03-12.dmp"));
|
||||||
|
Assert.Empty(later);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public void The_end_of_the_simulator_log_is_read_for_a_silent_crash()
|
public void The_end_of_the_simulator_log_is_read_for_a_silent_crash()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,6 +63,41 @@ public sealed class StartSimulation
|
|||||||
_log = log;
|
_log = log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Minidumps written since the simulator was started, newest first. The
|
||||||
|
/// simulator names them crash_*.dmp relative to its working directory - the
|
||||||
|
/// installation - and crashpad, where it is set up, keeps its own under
|
||||||
|
/// crashdumps; the folder of the binary is looked at too, for a simulator that
|
||||||
|
/// was once run from there.
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<string> CrashDumpsSince(DateTime startedUtc, string executable)
|
||||||
|
{
|
||||||
|
var dumps = new List<string>();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
foreach (string directory in new[] { _paths.Root, Path.GetDirectoryName(executable) ?? _paths.Root }
|
||||||
|
.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Where(_files.DirectoryExists))
|
||||||
|
dumps.AddRange(_files.GetFiles(directory, "*.dmp"));
|
||||||
|
|
||||||
|
string crashpad = _paths.FromRoot("crashdumps");
|
||||||
|
if (_files.DirectoryExists(crashpad))
|
||||||
|
dumps.AddRange(_files.GetFilesRecursive(crashpad, "*.dmp"));
|
||||||
|
|
||||||
|
// A little slack: the start is taken just after the launch, not before it.
|
||||||
|
var since = startedUtc - TimeSpan.FromSeconds(5);
|
||||||
|
return dumps.Distinct(StringComparer.OrdinalIgnoreCase)
|
||||||
|
.Where(dump => _files.GetLastWriteTimeUtc(dump) >= since)
|
||||||
|
.OrderByDescending(_files.GetLastWriteTimeUtc)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_log.Log("looking for crash dumps", ex);
|
||||||
|
return Array.Empty<string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>The end of the simulator's own log, for when it died without a word on its error stream.</summary>
|
/// <summary>The end of the simulator's own log, for when it died without a word on its error stream.</summary>
|
||||||
public IReadOnlyList<string> SimulatorLogTail(int count)
|
public IReadOnlyList<string> SimulatorLogTail(int count)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -522,14 +522,14 @@ public partial class MainWindow : Window
|
|||||||
GC.Collect();
|
GC.Collect();
|
||||||
GC.WaitForPendingFinalizers();
|
GC.WaitForPendingFinalizers();
|
||||||
|
|
||||||
WatchSimulator(result.Process);
|
WatchSimulator(result.Process, result.ExecutablePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Brings the starter back when the simulator exits. Adopts an already
|
/// Brings the starter back when the simulator exits. Adopts an already
|
||||||
/// running simulator when we did not start it ourselves.
|
/// running simulator when we did not start it ourselves.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void WatchSimulator(IProcessHandle? simulator)
|
private void WatchSimulator(IProcessHandle? simulator, string executable)
|
||||||
{
|
{
|
||||||
var started = DateTime.UtcNow;
|
var started = DateTime.UtcNow;
|
||||||
simulator ??= AppServices.Current.Processes.FindRunning(
|
simulator ??= AppServices.Current.Processes.FindRunning(
|
||||||
@@ -544,7 +544,7 @@ public partial class MainWindow : Window
|
|||||||
_ = simulator.WaitForExitAsync().ContinueWith(_ => Dispatcher.UIThread.Post(() =>
|
_ = simulator.WaitForExitAsync().ContinueWith(_ => Dispatcher.UIThread.Post(() =>
|
||||||
{
|
{
|
||||||
RestoreFromSimulator();
|
RestoreFromSimulator();
|
||||||
ReportIfCrashed(simulator, DateTime.UtcNow - started);
|
ReportIfCrashed(simulator, executable, started);
|
||||||
}), TaskScheduler.Default);
|
}), TaskScheduler.Default);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,13 +552,15 @@ public partial class MainWindow : Window
|
|||||||
/// Says why the simulator is gone when it did not end the way a session does:
|
/// Says why the simulator is gone when it did not end the way a session does:
|
||||||
/// an error code, or a close so soon after the start that nobody played - a
|
/// an error code, or a close so soon after the start that nobody played - a
|
||||||
/// window that only flashed. Its own words come from the error stream, or from
|
/// window that only flashed. Its own words come from the error stream, or from
|
||||||
/// its log when that stayed silent.
|
/// its log when that stayed silent, and a minidump it left is pointed at.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private async void ReportIfCrashed(IProcessHandle simulator, TimeSpan runTime)
|
private async void ReportIfCrashed(IProcessHandle simulator, string executable, DateTime startedUtc)
|
||||||
{
|
{
|
||||||
if (simulator.ExitCode is not { } code)
|
if (simulator.ExitCode is not { } code)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
var runTime = DateTime.UtcNow - startedUtc;
|
||||||
|
|
||||||
var exit = new SimulatorExit(code, runTime, simulator.ErrorTail);
|
var exit = new SimulatorExit(code, runTime, simulator.ErrorTail);
|
||||||
if (!exit.Failed)
|
if (!exit.Failed)
|
||||||
return;
|
return;
|
||||||
@@ -579,8 +581,16 @@ public partial class MainWindow : Window
|
|||||||
}
|
}
|
||||||
message.AppendLine().Append(App.Loc["SimExitSeeLog"]);
|
message.AppendLine().Append(App.Loc["SimExitSeeLog"]);
|
||||||
|
|
||||||
|
string? dump = AppServices.Current.StartSimulation.CrashDumpsSince(startedUtc, executable).FirstOrDefault();
|
||||||
|
if (dump is not null)
|
||||||
|
message.AppendLine().AppendLine().AppendLine(string.Format(App.Loc["SimExitDump"], dump))
|
||||||
|
.Append(App.Loc["SimExitOpenDump"]);
|
||||||
|
|
||||||
Diagnostics.Log(message.ToString().Replace(Environment.NewLine, " | "));
|
Diagnostics.Log(message.ToString().Replace(Environment.NewLine, " | "));
|
||||||
await MessageBox.Show(this, message.ToString(), App.Loc["SimExitTitle"], MessageBoxButtons.Ok);
|
bool open = await MessageBox.Show(this, message.ToString(), App.Loc["SimExitTitle"],
|
||||||
|
dump is null ? MessageBoxButtons.Ok : MessageBoxButtons.YesNo);
|
||||||
|
if (open && dump is not null && Path.GetDirectoryName(Path.GetFullPath(dump)) is { } folder)
|
||||||
|
AppServices.Current.Processes.OpenInShell(folder);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async void CheckExternalSettings()
|
private async void CheckExternalSettings()
|
||||||
|
|||||||
@@ -258,6 +258,8 @@
|
|||||||
<String key="SimExitQuick">Ukončil se {0:0.0} s po spuštění.</String>
|
<String key="SimExitQuick">Ukončil se {0:0.0} s po spuštění.</String>
|
||||||
<String key="SimExitMessages">Poslední zprávy:</String>
|
<String key="SimExitMessages">Poslední zprávy:</String>
|
||||||
<String key="SimExitSeeLog">Celý průběh je v souboru log.txt ve složce simulátoru.</String>
|
<String key="SimExitSeeLog">Celý průběh je v souboru log.txt ve složce simulátoru.</String>
|
||||||
|
<String key="SimExitDump">Simulátor uložil výpis paměti: {0}</String>
|
||||||
|
<String key="SimExitOpenDump">Otevřít složku s výpisem? Přiložte jej k hlášení chyby.</String>
|
||||||
<String key="TabInfo">Informace</String>
|
<String key="TabInfo">Informace</String>
|
||||||
<String key="SettingsFileChangedTitle">Nastavení</String>
|
<String key="SettingsFileChangedTitle">Nastavení</String>
|
||||||
<String key="SettingsFileChanged">Soubor eu07.ini byl změněn mimo starter. Načíst jej znovu?</String>
|
<String key="SettingsFileChanged">Soubor eu07.ini byl změněn mimo starter. Načíst jej znovu?</String>
|
||||||
|
|||||||
@@ -258,6 +258,8 @@
|
|||||||
<String key="SimExitQuick">It closed {0:0.0} s after starting.</String>
|
<String key="SimExitQuick">It closed {0:0.0} s after starting.</String>
|
||||||
<String key="SimExitMessages">Last messages:</String>
|
<String key="SimExitMessages">Last messages:</String>
|
||||||
<String key="SimExitSeeLog">The full run is in log.txt in the simulator folder.</String>
|
<String key="SimExitSeeLog">The full run is in log.txt in the simulator folder.</String>
|
||||||
|
<String key="SimExitDump">The simulator saved a memory dump: {0}</String>
|
||||||
|
<String key="SimExitOpenDump">Open the folder with the dump? Attach it to the bug report.</String>
|
||||||
<String key="TabInfo">Info</String>
|
<String key="TabInfo">Info</String>
|
||||||
<String key="SettingsFileChangedTitle">Settings</String>
|
<String key="SettingsFileChangedTitle">Settings</String>
|
||||||
<String key="SettingsFileChanged">eu07.ini was changed outside the starter. Reload it?</String>
|
<String key="SettingsFileChanged">eu07.ini was changed outside the starter. Reload it?</String>
|
||||||
|
|||||||
@@ -258,6 +258,8 @@
|
|||||||
<String key="SimExitQuick">{0:0.0} másodperccel az indítás után bezárult.</String>
|
<String key="SimExitQuick">{0:0.0} másodperccel az indítás után bezárult.</String>
|
||||||
<String key="SimExitMessages">Utolsó üzenetek:</String>
|
<String key="SimExitMessages">Utolsó üzenetek:</String>
|
||||||
<String key="SimExitSeeLog">A teljes futás a szimulátor mappájában lévő log.txt fájlban található.</String>
|
<String key="SimExitSeeLog">A teljes futás a szimulátor mappájában lévő log.txt fájlban található.</String>
|
||||||
|
<String key="SimExitDump">A szimulátor memóriaképet mentett: {0}</String>
|
||||||
|
<String key="SimExitOpenDump">Megnyitod a memóriaképet tartalmazó mappát? Csatold a hibajelentéshez.</String>
|
||||||
<String key="TabInfo">Információ</String>
|
<String key="TabInfo">Információ</String>
|
||||||
<String key="SettingsFileChangedTitle">Beállítások</String>
|
<String key="SettingsFileChangedTitle">Beállítások</String>
|
||||||
<String key="SettingsFileChanged">Az eu07.ini fájl a starteren kívül módosult. Újratölti?</String>
|
<String key="SettingsFileChanged">Az eu07.ini fájl a starteren kívül módosult. Újratölti?</String>
|
||||||
|
|||||||
@@ -258,6 +258,8 @@
|
|||||||
<String key="SimExitQuick">Zamknął się {0:0.0} s po uruchomieniu.</String>
|
<String key="SimExitQuick">Zamknął się {0:0.0} s po uruchomieniu.</String>
|
||||||
<String key="SimExitMessages">Ostatnie komunikaty:</String>
|
<String key="SimExitMessages">Ostatnie komunikaty:</String>
|
||||||
<String key="SimExitSeeLog">Pełny przebieg jest w pliku log.txt w katalogu symulatora.</String>
|
<String key="SimExitSeeLog">Pełny przebieg jest w pliku log.txt w katalogu symulatora.</String>
|
||||||
|
<String key="SimExitDump">Symulator zapisał zrzut pamięci: {0}</String>
|
||||||
|
<String key="SimExitOpenDump">Otworzyć folder ze zrzutem? Dołącz go do zgłoszenia błędu.</String>
|
||||||
<String key="TabInfo">Informacje</String>
|
<String key="TabInfo">Informacje</String>
|
||||||
<String key="SettingsFileChangedTitle">Ustawienia</String>
|
<String key="SettingsFileChangedTitle">Ustawienia</String>
|
||||||
<String key="SettingsFileChanged">Plik eu07.ini został zmieniony poza starterem. Wczytać ponownie?</String>
|
<String key="SettingsFileChanged">Plik eu07.ini został zmieniony poza starterem. Wczytać ponownie?</String>
|
||||||
|
|||||||
@@ -258,6 +258,8 @@
|
|||||||
<String key="SimExitQuick">Он закрылся через {0:0.0} с после запуска.</String>
|
<String key="SimExitQuick">Он закрылся через {0:0.0} с после запуска.</String>
|
||||||
<String key="SimExitMessages">Последние сообщения:</String>
|
<String key="SimExitMessages">Последние сообщения:</String>
|
||||||
<String key="SimExitSeeLog">Полный журнал находится в файле log.txt в папке симулятора.</String>
|
<String key="SimExitSeeLog">Полный журнал находится в файле log.txt в папке симулятора.</String>
|
||||||
|
<String key="SimExitDump">Симулятор сохранил дамп памяти: {0}</String>
|
||||||
|
<String key="SimExitOpenDump">Открыть папку с дампом? Приложите его к сообщению об ошибке.</String>
|
||||||
<String key="TabInfo">Информация</String>
|
<String key="TabInfo">Информация</String>
|
||||||
<String key="SettingsFileChangedTitle">Настройки</String>
|
<String key="SettingsFileChangedTitle">Настройки</String>
|
||||||
<String key="SettingsFileChanged">Файл eu07.ini изменён вне стартера. Загрузить заново?</String>
|
<String key="SettingsFileChanged">Файл eu07.ini изменён вне стартера. Загрузить заново?</String>
|
||||||
|
|||||||
Reference in New Issue
Block a user