mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2026-03-11 17:45:43 +00:00
Compare commits
5 commits
ebf521a278
...
43519944c4
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
43519944c4 | ||
|
|
4b42087bd4 | ||
|
|
55c2ae2b3d | ||
|
|
b51999a1ba | ||
|
|
bfc0d62732 |
4 changed files with 66 additions and 11 deletions
|
|
@ -22,10 +22,11 @@ namespace Ryujinx.Common.Utilities
|
||||||
}
|
}
|
||||||
|
|
||||||
// "dumpable" attribute of the calling process
|
// "dumpable" attribute of the calling process
|
||||||
|
private const int PR_GET_DUMPABLE = 3;
|
||||||
private const int PR_SET_DUMPABLE = 4;
|
private const int PR_SET_DUMPABLE = 4;
|
||||||
|
|
||||||
[DllImport("libc", SetLastError = true)]
|
[LibraryImport("libc", SetLastError = true)]
|
||||||
private static extern int prctl(int option, int arg2);
|
private static partial int prctl(int option, int arg2);
|
||||||
|
|
||||||
public static void SetCoreDumpable(bool dumpable)
|
public static void SetCoreDumpable(bool dumpable)
|
||||||
{
|
{
|
||||||
|
|
@ -36,5 +37,13 @@ namespace Ryujinx.Common.Utilities
|
||||||
Debug.Assert(result == 0);
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -570,6 +570,11 @@ namespace Ryujinx.HLE.HOS
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string DebugGetApplicationProcessMinidump()
|
||||||
|
{
|
||||||
|
return DebugGetApplicationProcess()?.Debugger?.GetMinidump();
|
||||||
|
}
|
||||||
|
|
||||||
internal KProcess DebugGetApplicationProcess()
|
internal KProcess DebugGetApplicationProcess()
|
||||||
{
|
{
|
||||||
lock (KernelContext.Processes)
|
lock (KernelContext.Processes)
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ namespace Ryujinx.Ava
|
||||||
public static bool PreviewerDetached { get; private set; }
|
public static bool PreviewerDetached { get; private set; }
|
||||||
public static bool UseHardwareAcceleration { get; private set; }
|
public static bool UseHardwareAcceleration { get; private set; }
|
||||||
public static string BackendThreadingArg { get; private set; }
|
public static string BackendThreadingArg { get; private set; }
|
||||||
|
public static bool CoreDumpArg { get; private set; }
|
||||||
|
|
||||||
private const uint MbIconwarning = 0x30;
|
private const uint MbIconwarning = 0x30;
|
||||||
|
|
||||||
|
|
@ -81,6 +82,8 @@ namespace Ryujinx.Ava
|
||||||
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
|
bool noGuiArg = ConsumeCommandLineArgument(ref args, "--no-gui") || ConsumeCommandLineArgument(ref args, "nogui");
|
||||||
bool coreDumpArg = ConsumeCommandLineArgument(ref args, "--core-dumps");
|
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.
|
// 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,
|
// 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.
|
// 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);
|
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)
|
foreach (Exception e in exceptions)
|
||||||
{
|
{
|
||||||
string message = $"Unhandled exception caught: {e}";
|
string message = $"Unhandled exception caught: {e}";
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
using Avalonia.Platform.Storage;
|
using Avalonia.Platform.Storage;
|
||||||
using Gommon;
|
using Gommon;
|
||||||
|
using Ryujinx.Common.Utilities;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
|
@ -11,29 +13,42 @@ namespace Ryujinx.Ava.Utilities
|
||||||
extension(IStorageProvider storageProvider)
|
extension(IStorageProvider storageProvider)
|
||||||
{
|
{
|
||||||
public Task<Optional<IStorageFolder>> OpenSingleFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
|
public Task<Optional<IStorageFolder>> OpenSingleFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
|
||||||
storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, false))
|
CoreDumpable(() => storageProvider.OpenFolderPickerAsync(FixOpenOptions(openOptions, false)))
|
||||||
.Then(folders => folders.FindFirst());
|
.Then(folders => folders.FindFirst());
|
||||||
|
|
||||||
public Task<Optional<IStorageFile>> OpenSingleFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
|
public Task<Optional<IStorageFile>> OpenSingleFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
|
||||||
storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, false))
|
CoreDumpable(() => storageProvider.OpenFilePickerAsync(FixOpenOptions(openOptions, false)))
|
||||||
.Then(files => files.FindFirst());
|
.Then(files => files.FindFirst());
|
||||||
|
|
||||||
public Task<Optional<IReadOnlyList<IStorageFolder>>> OpenMultiFolderPickerAsync(FolderPickerOpenOptions openOptions = null) =>
|
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);
|
.Then(folders => folders.Count > 0 ? Optional.Of(folders) : default);
|
||||||
|
|
||||||
public Task<Optional<IReadOnlyList<IStorageFile>>> OpenMultiFilePickerAsync(FilePickerOpenOptions openOptions = null) =>
|
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);
|
.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)
|
private static FilePickerOpenOptions FixOpenOptions(this FilePickerOpenOptions openOptions, bool allowMultiple)
|
||||||
{
|
{
|
||||||
if (openOptions is null)
|
if (openOptions is null)
|
||||||
return new FilePickerOpenOptions { AllowMultiple = allowMultiple };
|
return new FilePickerOpenOptions { AllowMultiple = allowMultiple };
|
||||||
|
|
||||||
openOptions.AllowMultiple = allowMultiple;
|
openOptions.AllowMultiple = allowMultiple;
|
||||||
|
|
||||||
return openOptions;
|
return openOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,7 +58,6 @@ namespace Ryujinx.Ava.Utilities
|
||||||
return new FolderPickerOpenOptions { AllowMultiple = allowMultiple };
|
return new FolderPickerOpenOptions { AllowMultiple = allowMultiple };
|
||||||
|
|
||||||
openOptions.AllowMultiple = allowMultiple;
|
openOptions.AllowMultiple = allowMultiple;
|
||||||
|
|
||||||
return openOptions;
|
return openOptions;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue