diff --git a/src/Ryujinx.Gtk3/UI/MainWindow.cs b/src/Ryujinx.Gtk3/UI/MainWindow.cs index 12c42f255..649fcd26a 100644 --- a/src/Ryujinx.Gtk3/UI/MainWindow.cs +++ b/src/Ryujinx.Gtk3/UI/MainWindow.cs @@ -679,7 +679,8 @@ namespace Ryujinx.UI ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, ConfigurationState.Instance.Multiplayer.Mode, ConfigurationState.Instance.Debug.EnableGdbStub, - ConfigurationState.Instance.Debug.GdbStubPort); + ConfigurationState.Instance.Debug.GdbStubPort, + ConfigurationState.Instance.Debug.DebuggerSuspendOnStart); _emulationContext = new HLE.Switch(configuration); } diff --git a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs index 2ed6eb796..9e029ac12 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs +++ b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.cs @@ -118,6 +118,7 @@ namespace Ryujinx.UI.Windows [GUI] ToggleButton _configureController8; [GUI] ToggleButton _configureControllerH; [GUI] ToggleButton _gdbStubToggle; + [GUI] ToggleButton _suspendOnStartToggle; [GUI] Adjustment _gdbStubPortSpinAdjustment; #pragma warning restore CS0649, IDE0044 @@ -322,6 +323,11 @@ namespace Ryujinx.UI.Windows _gdbStubToggle.Click(); } + if (ConfigurationState.Instance.Debug.DebuggerSuspendOnStart) + { + _suspendOnStartToggle.Click(); + } + // Custom EntryCompletion Columns. If added to glade, need to override more signals ListStore tzList = new(typeof(string), typeof(string), typeof(string)); _systemTimeZoneCompletion.Model = tzList; @@ -668,6 +674,7 @@ namespace Ryujinx.UI.Windows ConfigurationState.Instance.Graphics.ScalingFilterLevel.Value = (int)_scalingFilterLevel.Value; ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value = _multiLanSelect.ActiveId; ConfigurationState.Instance.Debug.EnableGdbStub.Value = _gdbStubToggle.Active; + ConfigurationState.Instance.Debug.DebuggerSuspendOnStart.Value = _suspendOnStartToggle.Active; ConfigurationState.Instance.Debug.GdbStubPort.Value = (ushort)_gdbStubPortSpinAdjustment.Value; _previousVolumeLevel = ConfigurationState.Instance.System.AudioVolume.Value; diff --git a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade index 22ccfe861..58817f065 100644 --- a/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade +++ b/src/Ryujinx.Gtk3/UI/Windows/SettingsWindow.glade @@ -3252,6 +3252,24 @@ 9 + + + Suspend application on start + True + True + False + Suspends the application before executing the first instruction, allowing for debugging from the earliest point. + start + 5 + 5 + True + + + False + True + 0 + + True diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index e9f535440..9045cae61 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -21,6 +21,7 @@ namespace Ryujinx.HLE.Debugger internal Switch Device { get; private set; } public ushort GdbStubPort { get; private set; } + public bool SuspendOnStart { get; private set; } private TcpListener ListenerSocket; private Socket ClientSocket = null; @@ -34,10 +35,11 @@ namespace Ryujinx.HLE.Debugger private ulong? cThread; private ulong? gThread; - public Debugger(Switch device, ushort port) + public Debugger(Switch device, ushort port, bool suspendOnStart) { Device = device; GdbStubPort = port; + SuspendOnStart = suspendOnStart; ARMeilleure.Optimizations.EnableDebugging = true; diff --git a/src/Ryujinx.HLE/HLEConfiguration.cs b/src/Ryujinx.HLE/HLEConfiguration.cs index 6eeca8ee2..368bcc9dd 100644 --- a/src/Ryujinx.HLE/HLEConfiguration.cs +++ b/src/Ryujinx.HLE/HLEConfiguration.cs @@ -179,6 +179,11 @@ namespace Ryujinx.HLE /// public ushort GdbStubPort { get; internal set; } + /// + /// Suspend execution when starting an application + /// + public bool DebuggerSuspendOnStart { get; internal set; } + public HLEConfiguration(VirtualFileSystem virtualFileSystem, LibHacHorizonManager libHacHorizonManager, ContentManager contentManager, @@ -206,7 +211,8 @@ namespace Ryujinx.HLE string multiplayerLanInterfaceId, MultiplayerMode multiplayerMode, bool enableGdbStub, - ushort gdbStubPort) + ushort gdbStubPort, + bool debuggerSuspendOnStart) { VirtualFileSystem = virtualFileSystem; LibHacHorizonManager = libHacHorizonManager; @@ -236,6 +242,7 @@ namespace Ryujinx.HLE MultiplayerMode = multiplayerMode; EnableGdbStub = enableGdbStub; GdbStubPort = gdbStubPort; + DebuggerSuspendOnStart = debuggerSuspendOnStart; } } } diff --git a/src/Ryujinx.HLE/Switch.cs b/src/Ryujinx.HLE/Switch.cs index e1e386181..05d07c6ec 100644 --- a/src/Ryujinx.HLE/Switch.cs +++ b/src/Ryujinx.HLE/Switch.cs @@ -54,7 +54,7 @@ namespace Ryujinx.HLE AudioDeviceDriver = new CompatLayerHardwareDeviceDriver(Configuration.AudioDeviceDriver); Memory = new MemoryBlock(Configuration.MemoryConfiguration.ToDramSize(), memoryAllocationFlags); Gpu = new GpuContext(Configuration.GpuRenderer); - Debugger = Configuration.EnableGdbStub ? new Debugger.Debugger(this, Configuration.GdbStubPort) : null; + Debugger = Configuration.EnableGdbStub ? new Debugger.Debugger(this, Configuration.GdbStubPort, Configuration.DebuggerSuspendOnStart) : null; System = new HOS.Horizon(this); Statistics = new PerformanceStatistics(); Hid = new Hid(this, System.HidStorage); diff --git a/src/Ryujinx.Headless.SDL2/Options.cs b/src/Ryujinx.Headless.SDL2/Options.cs index e76515efc..323ca60ce 100644 --- a/src/Ryujinx.Headless.SDL2/Options.cs +++ b/src/Ryujinx.Headless.SDL2/Options.cs @@ -233,6 +233,9 @@ namespace Ryujinx.Headless.SDL2 [Option("gdb-stub-port", Required = false, Default = 55555, HelpText = "Specifies which TCP port the GDB stub listens on.")] public ushort GdbStubPort { get; set; } + [Option("suspend-on-start", Required = false, Default = false, HelpText = "Suspend execution when starting an application.")] + public bool DebuggerSuspendOnStart { get; set; } + // Values [Value(0, MetaName = "input", HelpText = "Input to load.", Required = true)] diff --git a/src/Ryujinx.Headless.SDL2/Program.cs b/src/Ryujinx.Headless.SDL2/Program.cs index d6cf7d6d6..e63d1ff3c 100644 --- a/src/Ryujinx.Headless.SDL2/Program.cs +++ b/src/Ryujinx.Headless.SDL2/Program.cs @@ -573,7 +573,8 @@ namespace Ryujinx.Headless.SDL2 options.MultiplayerLanInterfaceId, Common.Configuration.Multiplayer.MultiplayerMode.Disabled, options.EnableGdbStub, - options.GdbStubPort); + options.GdbStubPort, + options.DebuggerSuspendOnStart); return new Switch(configuration); } diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs index e90e58f2c..a63d17145 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationFileFormat.cs @@ -396,6 +396,11 @@ namespace Ryujinx.UI.Common.Configuration /// public ushort GdbStubPort { get; set; } + /// + /// Which TCP port should the GDB stub listen on + /// + public ushort DebuggerSuspendOnStart { get; set; } + /// /// Loads a configuration file from disk /// diff --git a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs index fe96420c4..255175fa3 100644 --- a/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs +++ b/src/Ryujinx.UI.Common/Configuration/ConfigurationState.cs @@ -592,12 +592,19 @@ namespace Ryujinx.UI.Common.Configuration /// public ReactiveObject GdbStubPort { get; private set; } + /// + /// Suspend execution when starting an application + /// + public ReactiveObject DebuggerSuspendOnStart { get; private set; } + public DebugSection() { EnableGdbStub = new ReactiveObject(); EnableGdbStub.Event += static (sender, e) => LogValueChange(e, nameof(EnableGdbStub)); GdbStubPort = new ReactiveObject(); GdbStubPort.Event += static (sender, e) => LogValueChange(e, nameof(GdbStubPort)); + DebuggerSuspendOnStart = new ReactiveObject(); + DebuggerSuspendOnStart.Event += static (sender, e) => LogValueChange(e, nameof(DebuggerSuspendOnStart)); } } diff --git a/src/Ryujinx/AppHost.cs b/src/Ryujinx/AppHost.cs index 2e847c620..7dcece17b 100644 --- a/src/Ryujinx/AppHost.cs +++ b/src/Ryujinx/AppHost.cs @@ -871,7 +871,8 @@ namespace Ryujinx.Ava ConfigurationState.Instance.Multiplayer.LanInterfaceId.Value, ConfigurationState.Instance.Multiplayer.Mode, ConfigurationState.Instance.Debug.EnableGdbStub.Value, - ConfigurationState.Instance.Debug.GdbStubPort.Value); + ConfigurationState.Instance.Debug.GdbStubPort.Value, + ConfigurationState.Instance.Debug.DebuggerSuspendOnStart.Value); Device = new Switch(configuration); } diff --git a/src/Ryujinx/Assets/Locales/en_US.json b/src/Ryujinx/Assets/Locales/en_US.json index 00d0aa40d..237cb710e 100644 --- a/src/Ryujinx/Assets/Locales/en_US.json +++ b/src/Ryujinx/Assets/Locales/en_US.json @@ -783,7 +783,9 @@ "MultiplayerModeLdnMitm": "ldn_mitm", "SettingsTabDebug": "Debug", "SettingsTabDebugTitle": "Debug (WARNING: For developer use only)", - "SettingsTabDebugEnableGDBStub": "Enable GDB Stub", + "EnableGDBStub": "Enable GDB Stub", "GDBStubToggleTooltip": "Enables the GDB stub which makes it possible to debug the running application. For development use only!", - "GDBStubPort": "GDB stub port:" + "GDBStubPort": "GDB stub port:", + "DebuggerSuspendOnStart": "Suspend application on start", + "DebuggerSuspendOnStartTooltip": "Suspends the application before executing the first instruction, allowing for debugging from the earliest point." } diff --git a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs index 88a4ab91b..d48ec95a2 100644 --- a/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs +++ b/src/Ryujinx/UI/ViewModels/SettingsViewModel.cs @@ -56,6 +56,7 @@ namespace Ryujinx.Ava.UI.ViewModels private int _multiplayerModeIndex; private bool _enableGDBStub; private ushort _gdbStubPort; + private bool _debuggerSuspendOnStart; public int ResolutionScale { @@ -281,6 +282,16 @@ namespace Ryujinx.Ava.UI.ViewModels } } + public bool DebuggerSuspendOnStart + { + get => _debuggerSuspendOnStart; + set + { + _debuggerSuspendOnStart = value; + ConfigurationState.Instance.Debug.DebuggerSuspendOnStart.Value = _debuggerSuspendOnStart; + } + } + public SettingsViewModel(VirtualFileSystem virtualFileSystem, ContentManager contentManager) : this() { _virtualFileSystem = virtualFileSystem; diff --git a/src/Ryujinx/UI/Views/Settings/SettingsDebugView.axaml b/src/Ryujinx/UI/Views/Settings/SettingsDebugView.axaml index d47d8d8e6..746842a87 100644 --- a/src/Ryujinx/UI/Views/Settings/SettingsDebugView.axaml +++ b/src/Ryujinx/UI/Views/Settings/SettingsDebugView.axaml @@ -30,7 +30,7 @@ HorizontalAlignment="Stretch" Orientation="Vertical"> - @@ -48,6 +48,13 @@ Minimum="1024" Maximum="65535" /> + + + + +