Merge branch ryujinx:master into master

This commit is contained in:
Babib3l 2025-10-21 14:47:14 +02:00
commit 75faee906d
15 changed files with 135 additions and 56 deletions

View file

@ -64,7 +64,7 @@ namespace Ryujinx.HLE.Debugger
Logger.Notice.Print(LogClass.GdbStub, "NACK received!"); Logger.Notice.Print(LogClass.GdbStub, "NACK received!");
continue; continue;
case '\x03': case '\x03':
_messages.Add(StatelessMessage.BreakIn); _messages.Add(Message.BreakIn);
break; break;
case '$': case '$':
string cmd = string.Empty; string cmd = string.Empty;
@ -85,7 +85,7 @@ namespace Ryujinx.HLE.Debugger
} }
else else
{ {
_messages.Add(StatelessMessage.SendNack); _messages.Add(Message.SendNack);
} }
break; break;

View file

@ -14,16 +14,16 @@ namespace Ryujinx.HLE.Debugger
{ {
switch (_messages.Take()) switch (_messages.Take())
{ {
case StatelessMessage { Type: MessageType.BreakIn }: case Message { Type: MessageType.BreakIn }:
Logger.Notice.Print(LogClass.GdbStub, "Break-in requested"); Logger.Notice.Print(LogClass.GdbStub, "Break-in requested");
_commands.Interrupt(); _commands.Interrupt();
break; break;
case StatelessMessage { Type: MessageType.SendNack }: case Message { Type: MessageType.SendNack }:
_writeStream.WriteByte((byte)'-'); _writeStream.WriteByte((byte)'-');
break; break;
case StatelessMessage { Type: MessageType.Kill }: case Message { Type: MessageType.Kill }:
return; return;
case CommandMessage { Command: { } cmd }: case CommandMessage { Command: { } cmd }:

View file

@ -1,13 +1,49 @@
using Gommon;
using JetBrains.Annotations;
using Ryujinx.Common.Logging; using Ryujinx.Common.Logging;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using System; using System;
using System.Collections.Generic;
using System.Linq;
using System.Text; using System.Text;
namespace Ryujinx.HLE.Debugger namespace Ryujinx.HLE.Debugger
{ {
public partial class Debugger public partial class Debugger
{ {
static Debugger()
{
_rcmdDelegates.Add(["help"],
_ => _rcmdDelegates.Keys
.Where(x => !x[0].Equals("help"))
.Select(x => x.JoinToString('\n'))
.JoinToString('\n') + '\n'
);
_rcmdDelegates.Add(["get info"], dbgr => dbgr.GetProcessInfo());
_rcmdDelegates.Add(["backtrace", "bt"], dbgr => dbgr.GetStackTrace());
_rcmdDelegates.Add(["registers", "reg"], dbgr => dbgr.GetRegisters());
_rcmdDelegates.Add(["minidump"], dbgr => dbgr.GetMinidump());
}
private static readonly Dictionary<string[], Func<Debugger, string>> _rcmdDelegates = new();
public static Func<Debugger, string> FindRcmdDelegate(string command)
{
Func<Debugger, string> searchResult = _ => $"Unknown command: {command}\n";
foreach ((string[] names, Func<Debugger, string> dlg) in _rcmdDelegates)
{
if (names.ContainsIgnoreCase(command.Trim()))
{
searchResult = dlg;
break;
}
}
return searchResult;
}
public string GetStackTrace() public string GetStackTrace()
{ {
if (GThread == null) if (GThread == null)

View file

@ -4,10 +4,8 @@ using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.Threading; using Ryujinx.HLE.HOS.Kernel.Threading;
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.IO;
using System.Linq; using System.Linq;
using System.Net.Sockets; using System.Net.Sockets;
using System.Text;
using System.Threading; using System.Threading;
using IExecutionContext = Ryujinx.Cpu.IExecutionContext; using IExecutionContext = Ryujinx.Cpu.IExecutionContext;
@ -20,7 +18,7 @@ namespace Ryujinx.HLE.Debugger
public ushort GdbStubPort { get; private set; } public ushort GdbStubPort { get; private set; }
private readonly BlockingCollection<IMessage> _messages = new(1); private readonly BlockingCollection<Message.IMarker> _messages = new(1);
private readonly Thread _debuggerThread; private readonly Thread _debuggerThread;
private readonly Thread _messageHandlerThread; private readonly Thread _messageHandlerThread;
@ -59,8 +57,8 @@ namespace Ryujinx.HLE.Debugger
internal KThread[] GetThreads() => DebugProcess.ThreadUids.Select(DebugProcess.GetThread).ToArray(); internal KThread[] GetThreads() => DebugProcess.ThreadUids.Select(DebugProcess.GetThread).ToArray();
internal bool IsProcess32Bit => DebugProcess.GetThread(GThread.Value).Context.IsAarch32; internal bool IsProcess32Bit => DebugProcess.GetThread(GThread ?? DebugProcess.ThreadUids.First()).Context.IsAarch32;
internal bool WriteRegister(IExecutionContext ctx, int registerId, StringStream ss) => internal bool WriteRegister(IExecutionContext ctx, int registerId, StringStream ss) =>
IsProcess32Bit IsProcess32Bit
? ctx.WriteRegister32(registerId, ss) ? ctx.WriteRegister32(registerId, ss)
@ -89,7 +87,7 @@ namespace Ryujinx.HLE.Debugger
_readStream?.Close(); _readStream?.Close();
_writeStream?.Close(); _writeStream?.Close();
_debuggerThread.Join(); _debuggerThread.Join();
_messages.Add(StatelessMessage.Kill); _messages.Add(Message.Kill);
_messageHandlerThread.Join(); _messageHandlerThread.Join();
_messages.Dispose(); _messages.Dispose();
_breakHandlerEvent.Dispose(); _breakHandlerEvent.Dispose();

View file

@ -20,6 +20,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
Commands = commands; Commands = commands;
} }
public void ReplyHex(string data) => Reply(Helpers.ToHex(data));
public void ReplyHex(byte[] data) => Reply(Helpers.ToHex(data));
public void Reply(string cmd) public void Reply(string cmd)
{ {
Logger.Debug?.Print(LogClass.GdbStub, $"Reply: {cmd}"); Logger.Debug?.Print(LogClass.GdbStub, $"Reply: {cmd}");
@ -146,6 +149,12 @@ namespace Ryujinx.HLE.Debugger.Gdb
break; break;
} }
if (ss.ConsumeRemaining("Attached"))
{
Reply("1");
break;
}
if (ss.ConsumeRemaining("ProcessInfo")) if (ss.ConsumeRemaining("ProcessInfo"))
{ {
Reply( Reply(
@ -191,11 +200,10 @@ namespace Ryujinx.HLE.Debugger.Gdb
break; break;
} }
Reply(Helpers.ToHex( ReplyHex(
DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value)) DebugProcess.IsThreadPaused(DebugProcess.GetThread(threadId.Value))
? "Paused" ? "Paused"
: "Running" : "Running"
)
); );
break; break;

View file

@ -83,12 +83,13 @@ namespace Ryujinx.HLE.Debugger.Gdb
} }
Debugger.DebugProcess.DebugContinue(); Debugger.DebugProcess.DebugContinue();
Processor.ReplyOK();
} }
internal void Detach() internal void Detach()
{ {
Debugger.BreakpointManager.ClearAll(); Debugger.BreakpointManager.ClearAll();
Continue(null); Continue(null); // Continue() will call ReplyError/ReplyOK for us.
} }
internal void ReadRegisters() internal void ReadRegisters()
@ -196,7 +197,7 @@ namespace Ryujinx.HLE.Debugger.Gdb
{ {
byte[] data = new byte[len]; byte[] data = new byte[len];
Debugger.DebugProcess.CpuMemory.Read(addr, data); Debugger.DebugProcess.CpuMemory.Read(addr, data);
Processor.Reply(Helpers.ToHex(data)); Processor.ReplyHex(data);
} }
catch (InvalidMemoryRegionException) catch (InvalidMemoryRegionException)
{ {
@ -421,17 +422,9 @@ namespace Ryujinx.HLE.Debugger.Gdb
string command = Helpers.FromHex(hexCommand); string command = Helpers.FromHex(hexCommand);
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}"); Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
string response = command.Trim().ToLowerInvariant() switch Func<Debugger, string> rcmd = Debugger.FindRcmdDelegate(command);
{
"help" => "backtrace\nbt\nregisters\nreg\nget info\nminidump\n",
"get info" => Debugger.GetProcessInfo(),
"backtrace" or "bt" => Debugger.GetStackTrace(),
"registers" or "reg" => Debugger.GetRegisters(),
"minidump" => Debugger.GetMinidump(),
_ => $"Unknown command: {command}\n"
};
Processor.Reply(Helpers.ToHex(response)); Processor.ReplyHex(rcmd(Debugger));
} }
catch (Exception e) catch (Exception e)
{ {

View file

@ -20,8 +20,8 @@ namespace Ryujinx.HLE.Debugger.Gdb
32 => Helpers.ToHex(BitConverter.GetBytes(state.DebugPc)), 32 => Helpers.ToHex(BitConverter.GetBytes(state.DebugPc)),
33 => Helpers.ToHex(BitConverter.GetBytes(state.Pstate)), 33 => Helpers.ToHex(BitConverter.GetBytes(state.Pstate)),
>= 34 and <= 65 => Helpers.ToHex(state.GetV(registerId - 34).ToArray()), >= 34 and <= 65 => Helpers.ToHex(state.GetV(registerId - 34).ToArray()),
66 => Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpsr)), 66 => Helpers.ToHex(BitConverter.GetBytes(state.Fpsr)),
67 => Helpers.ToHex(BitConverter.GetBytes((uint)state.Fpcr)), 67 => Helpers.ToHex(BitConverter.GetBytes(state.Fpcr)),
_ => null _ => null
}; };

View file

@ -6,6 +6,10 @@ namespace Ryujinx.HLE.Debugger
{ {
internal interface IDebuggableProcess internal interface IDebuggableProcess
{ {
IVirtualMemoryManager CpuMemory { get; }
ulong[] ThreadUids { get; }
DebugState DebugState { get; }
void DebugStop(); void DebugStop();
void DebugContinue(); void DebugContinue();
void DebugContinue(KThread thread); void DebugContinue(KThread thread);
@ -13,9 +17,6 @@ namespace Ryujinx.HLE.Debugger
KThread GetThread(ulong threadUid); KThread GetThread(ulong threadUid);
bool IsThreadPaused(KThread thread); bool IsThreadPaused(KThread thread);
public void DebugInterruptHandler(IExecutionContext ctx); public void DebugInterruptHandler(IExecutionContext ctx);
IVirtualMemoryManager CpuMemory { get; }
ulong[] ThreadUids { get; }
DebugState DebugState { get; }
void InvalidateCacheRegion(ulong address, ulong size); void InvalidateCacheRegion(ulong address, ulong size);
} }
} }

View file

@ -2,11 +2,6 @@ using Ryujinx.Cpu;
namespace Ryujinx.HLE.Debugger namespace Ryujinx.HLE.Debugger
{ {
/// <summary>
/// Marker interface for debugger messages.
/// </summary>
interface IMessage;
public enum MessageType public enum MessageType
{ {
Kill, Kill,
@ -14,14 +9,19 @@ namespace Ryujinx.HLE.Debugger
SendNack SendNack
} }
record struct StatelessMessage(MessageType Type) : IMessage record struct Message(MessageType Type) : Message.IMarker
{ {
public static StatelessMessage Kill => new(MessageType.Kill); /// <summary>
public static StatelessMessage BreakIn => new(MessageType.BreakIn); /// Marker interface for debugger messages.
public static StatelessMessage SendNack => new(MessageType.SendNack); /// </summary>
internal interface IMarker;
public static Message Kill => new(MessageType.Kill);
public static Message BreakIn => new(MessageType.BreakIn);
public static Message SendNack => new(MessageType.SendNack);
} }
struct CommandMessage : IMessage struct CommandMessage : Message.IMarker
{ {
public readonly string Command; public readonly string Command;
@ -31,7 +31,7 @@ namespace Ryujinx.HLE.Debugger
} }
} }
public class ThreadBreakMessage : IMessage public class ThreadBreakMessage : Message.IMarker
{ {
public IExecutionContext Context { get; } public IExecutionContext Context { get; }
public ulong Address { get; } public ulong Address { get; }

View file

@ -38,6 +38,13 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
} }
[CommandCmif(3)] [CommandCmif(3)]
// LoadIdTokenCacheDeprecated() -> (u32 id_token_cache_size, buffer<bytes, 6>)
public ResultCode LoadIdTokenCacheDeprecated(ServiceCtx context)
{
return _managerServer.LoadIdTokenCache(context);
}
[CommandCmif(4)] // 19.0.0+
// LoadIdTokenCache() -> (u32 id_token_cache_size, buffer<bytes, 6>) // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer<bytes, 6>)
public ResultCode LoadIdTokenCache(ServiceCtx context) public ResultCode LoadIdTokenCache(ServiceCtx context)
{ {

View file

@ -38,6 +38,13 @@ namespace Ryujinx.HLE.HOS.Services.Account.Acc.AccountService
} }
[CommandCmif(3)] [CommandCmif(3)]
// LoadIdTokenCacheDeprecated() -> (u32 id_token_cache_size, buffer<bytes, 6>)
public ResultCode LoadIdTokenCacheDeprecated(ServiceCtx context)
{
return _managerServer.LoadIdTokenCache(context);
}
[CommandCmif(4)] // 19.0.0+
// LoadIdTokenCache() -> (u32 id_token_cache_size, buffer<bytes, 6>) // LoadIdTokenCache() -> (u32 id_token_cache_size, buffer<bytes, 6>)
public ResultCode LoadIdTokenCache(ServiceCtx context) public ResultCode LoadIdTokenCache(ServiceCtx context)
{ {

View file

@ -83,23 +83,23 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public ReadOnlySpan<AtomicStorage<T>> ReadEntries(uint maxCount) public ReadOnlySpan<AtomicStorage<T>> ReadEntries(uint maxCount)
{ {
ulong countAvailaible = Math.Min(Math.Max(0, ReadCurrentCount()), maxCount); ulong countAvailable = Math.Min(Math.Max(0, ReadCurrentCount()), maxCount);
if (countAvailaible == 0) if (countAvailable == 0)
{ {
return ReadOnlySpan<AtomicStorage<T>>.Empty; return ReadOnlySpan<AtomicStorage<T>>.Empty;
} }
ulong index = ReadCurrentIndex(); ulong index = ReadCurrentIndex();
AtomicStorage<T>[] result = new AtomicStorage<T>[countAvailaible]; AtomicStorage<T>[] result = new AtomicStorage<T>[countAvailable];
Span<AtomicStorage<T>> storageSpan = _storage.AsSpan(); Span<AtomicStorage<T>> storageSpan = _storage.AsSpan();
for (ulong i = 0; i < countAvailaible; i++) for (ulong i = 0; i < countAvailable; i++)
{ {
int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailaible + i) % MaxEntries); int inputEntryIndex = (int)((index + MaxEntries + 1 - countAvailable + i) % MaxEntries);
int outputEntryIndex = (int)(countAvailaible - i - 1); int outputEntryIndex = (int)(countAvailable - i - 1);
ulong samplingNumber0 = storageSpan[inputEntryIndex].ReadSamplingNumberAtomic(); ulong samplingNumber0 = storageSpan[inputEntryIndex].ReadSamplingNumberAtomic();
result[outputEntryIndex] = storageSpan[inputEntryIndex]; result[outputEntryIndex] = storageSpan[inputEntryIndex];
@ -107,9 +107,9 @@ namespace Ryujinx.HLE.HOS.Services.Hid.Types.SharedMemory.Common
if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1)) if (samplingNumber0 != samplingNumber1 && (i > 0 && (result[outputEntryIndex].SamplingNumber - result[outputEntryIndex].SamplingNumber) != 1))
{ {
ulong tempCount = Math.Min(ReadCurrentCount(), countAvailaible); ulong tempCount = Math.Min(ReadCurrentCount(), countAvailable);
countAvailaible = Math.Min(tempCount, maxCount); countAvailable = Math.Min(tempCount, maxCount);
index = ReadCurrentIndex(); index = ReadCurrentIndex();
i -= 1; i -= 1;

View file

@ -15,5 +15,14 @@ namespace Ryujinx.HLE.HOS.Services.Ldn
return ResultCode.Success; return ResultCode.Success;
} }
[CommandCmif(1)] // 18.0.0+
// CreateClientProcessMonitor() -> object<nn::ldn::detail::IClientProcessMonitor>
public ResultCode CreateClientProcessMonitor(ServiceCtx context)
{
MakeObject(context, new IClientProcessMonitor(context));
return ResultCode.Success;
}
} }
} }

View file

@ -0,0 +1,20 @@
using Ryujinx.Common.Logging;
namespace Ryujinx.HLE.HOS.Services.Ldn.UserServiceCreator
{
class IClientProcessMonitor : DisposableIpcService
{
public IClientProcessMonitor(ServiceCtx context) { }
[CommandCmif(0)] // 18.0.0+
// RegisterClient(u64 pid_placeholder, pid)
public ResultCode RegisterClient(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceLdn);
return ResultCode.Success;
}
protected override void Dispose(bool isDisposing) { }
}
}

View file

@ -59,18 +59,18 @@ namespace Ryujinx.HLE.UI.Input
} }
} }
public void Update(bool supressEvents = false) public void Update(bool suppressEvents = false)
{ {
int npadsCount = _device.Hid.SharedMemory.Npads.Length; int npadsCount = _device.Hid.SharedMemory.Npads.Length;
// Process each input individually. // Process each input individually.
for (int npadIndex = 0; npadIndex < npadsCount; npadIndex++) for (int npadIndex = 0; npadIndex < npadsCount; npadIndex++)
{ {
UpdateNpad(npadIndex, supressEvents); UpdateNpad(npadIndex, suppressEvents);
} }
} }
private void UpdateNpad(int npadIndex, bool supressEvents) private void UpdateNpad(int npadIndex, bool suppressEvents)
{ {
const int MaxEntries = 1024; const int MaxEntries = 1024;
@ -103,7 +103,7 @@ namespace Ryujinx.HLE.UI.Input
break; break;
} }
if (!supressEvents) if (!suppressEvents)
{ {
ProcessNpadButtons(npadIndex, entry.Object.Buttons); ProcessNpadButtons(npadIndex, entry.Object.Buttons);
} }