Compare commits

...

5 commits

Author SHA1 Message Date
Coxxs
43519944c4 Merge branch 'exception-minidump' into 'master'
Create minidump at ProcessUnhandledException

See merge request [ryubing/ryujinx!217](https://git.ryujinx.app/ryubing/ryujinx/-/merge_requests/217)
2026-03-10 15:15:34 -05:00
KeatonTheBot
4b42087bd4 Linux: Fix file picker not launching from disabling core dumps (ryubing/ryujinx!249)
Some checks failed
Canary CI / Release for linux-arm64 (push) Has been cancelled
Canary CI / Release for linux-x64 (push) Has been cancelled
Canary CI / Release for win-x64 (push) Has been cancelled
Canary CI / Release MacOS universal (push) Has been cancelled
Canary CI / Create GitLab Release (push) Has been cancelled
See merge request ryubing/ryujinx!249
2026-03-06 19:04:42 -06:00
Coxxs
55c2ae2b3d Check if Switch is running before creating minidump 2025-12-22 12:28:21 +08:00
Coxxs
b51999a1ba Print a message first in case it crashes again during minidump creation 2025-12-22 12:28:21 +08:00
Coxxs
bfc0d62732 Create minidump at ProcessUnhandledException 2025-12-22 12:28:21 +08:00
4 changed files with 66 additions and 11 deletions

View file

@ -22,10 +22,11 @@ namespace Ryujinx.Common.Utilities
}
// "dumpable" attribute of the calling process
private const int PR_GET_DUMPABLE = 3;
private const int PR_SET_DUMPABLE = 4;
[DllImport("libc", SetLastError = true)]
private static extern int prctl(int option, int arg2);
[LibraryImport("libc", SetLastError = true)]
private static partial int prctl(int option, int arg2);
public static void SetCoreDumpable(bool dumpable)
{
@ -36,5 +37,13 @@ namespace Ryujinx.Common.Utilities
Debug.Assert(result == 0);
}
}
// Use the below line to display dumpable status in the console:
// Console.WriteLine($"{OsUtils.IsCoreDumpable()}");
public static bool IsCoreDumpable()
{
int result = prctl(PR_GET_DUMPABLE, 0);
return result == 1;
}
}
}

View file

@ -570,6 +570,11 @@ namespace Ryujinx.HLE.HOS
}
}
public string DebugGetApplicationProcessMinidump()
{
return DebugGetApplicationProcess()?.Debugger?.GetMinidump();
}
internal KProcess DebugGetApplicationProcess()
{
lock (KernelContext.Processes)

View file

@ -42,6 +42,7 @@ namespace Ryujinx.Ava
public static bool PreviewerDetached { get; private set; }
public static bool UseHardwareAcceleration { get; private set; }
public static string BackendThreadingArg { get; private set; }
public static bool CoreDumpArg { get; private set; }
private const uint MbIconwarning = 0x30;
@ -81,6 +82,8 @@ namespace Ryujinx.Ava
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps");
CoreDumpArg = coreDumpArg;
// TODO: Ryujinx causes core dumps on Linux when it exits "uncleanly", eg. through an unhandled exception.
// This is undesirable and causes very odd behavior during development (the process stops responding,
// the .NET debugger freezes or suddenly detaches, /tmp/ gets filled etc.), unless explicitly requested by the user.
@ -383,6 +386,30 @@ namespace Ryujinx.Ava
exceptions.Add(initialException);
}
if (isTerminating && HLE.Switch.Shared is { } device)
{
try
{
// Print a short message first just in case it crashes again during minidump creation (should not happen)
Logger.Error?.Print(LogClass.Application, $"Unhandled exception caught: {initialException.GetType().Name}. Creating guest program minidump...");
var minidump = device.System?.DebugGetApplicationProcessMinidump();
if (minidump == null)
{
Logger.Warning?.Print(LogClass.Application, "Failed to create minidump");
}
else
{
Logger.Info?.Print(LogClass.Application, minidump);
}
}
catch (Exception e)
{
Logger.Error?.Print(LogClass.Application, $"Failed to create minidump: {e.Message}");
}
}
foreach (Exception e in exceptions)
{
string message = $"Unhandled exception caught: {e}";

View file

@ -1,5 +1,7 @@
using Avalonia.Platform.Storage;
using Gommon;
using Ryujinx.Common.Utilities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@ -11,29 +13,42 @@ namespace Ryujinx.Ava.Utilities
extension(IStorageProvider storageProvider)
{
public Task<Optional<IStorageFolder>> OpenSingleFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, false))
CoreDumpable(() => storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, false)))
.Then(folders => folders.FindFirst());
public Task<Optional<IStorageFile>> OpenSingleFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, false))
CoreDumpable(() => storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, false)))
.Then(files => files.FindFirst());
public Task<Optional<IReadOnlyList<IStorageFolder>>> OpenMultiFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, true))
CoreDumpable(() => storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, true)))
.Then(folders => folders.Count > 0 ? Optional.Of(folders) : default);
public Task<Optional<IReadOnlyList<IStorageFile>>> OpenMultiFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, true))
CoreDumpable(() => storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, true)))
.Then(files => files.Count > 0 ? Optional.Of(files) : default);
}
private static async Task<T> CoreDumpable<T>(Func<Task<T>> picker)
{
OsUtils.SetCoreDumpable(true);
try
{
return await picker();
}
finally
{
if (!Program.CoreDumpArg)
OsUtils.SetCoreDumpable(false);
}
}
private static FilePickerOpenOptions FixOpenOptions(this FilePickerOpenOptions openOptions, bool allowMultiple)
{
if (openOptions is null)
return new FilePickerOpenOptions { AllowMultiple = allowMultiple };
openOptions.AllowMultiple = allowMultiple;
return openOptions;
}
@ -43,7 +58,6 @@ namespace Ryujinx.Ava.Utilities
return new FolderPickerOpenOptions { AllowMultiple = allowMultiple };
openOptions.AllowMultiple = allowMultiple;
return openOptions;
}
}