From 37ba4b8cd3214af3ddd3a252166db8d4221fe3b5 Mon Sep 17 00:00:00 2001 From: Coxxs <58-coxxs@users.noreply.git.ryujinx.app> Date: Sat, 21 Jun 2025 09:37:36 +0800 Subject: [PATCH 1/3] gdb: Update DebugPc during SVC call and break --- src/ARMeilleure/State/ExecutionContext.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/ARMeilleure/State/ExecutionContext.cs b/src/ARMeilleure/State/ExecutionContext.cs index 0b0a83c9f..c44c6e062 100644 --- a/src/ARMeilleure/State/ExecutionContext.cs +++ b/src/ARMeilleure/State/ExecutionContext.cs @@ -164,11 +164,21 @@ namespace ARMeilleure.State internal void OnBreak(ulong address, int imm) { + if (Optimizations.EnableDebugging) + { + DebugPc = Pc; // TODO: Is this the best place to update DebugPc? + } + _breakCallback?.Invoke(this, address, imm); } internal void OnSupervisorCall(ulong address, int imm) { + if (Optimizations.EnableDebugging) + { + DebugPc = Pc; // TODO: Is this the best place to update DebugPc? + } + _supervisorCallback?.Invoke(this, address, imm); } From db22c3af2559aa362b8fc919883f76df5450904c Mon Sep 17 00:00:00 2001 From: Coxxs <58-coxxs@users.noreply.git.ryujinx.app> Date: Sat, 21 Jun 2025 09:43:26 +0800 Subject: [PATCH 2/3] gdb: Set correct gThread and cThread when break --- src/Ryujinx.HLE/Debugger/Debugger.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index 608ab977a..dd6394e60 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -916,6 +916,7 @@ namespace Ryujinx.HLE.Debugger public void BreakHandler(IExecutionContext ctx, ulong address, int imm) { + gThread = cThread = ctx.ThreadUid; Logger.Notice.Print(LogClass.GdbStub, $"Break hit on thread {ctx.ThreadUid} at pc {address:x016}"); Messages.Add(new ThreadBreakMessage(ctx, address, imm)); From 22957b91404c9a31255b347ffac127f8b199416e Mon Sep 17 00:00:00 2001 From: Coxxs <58-coxxs@users.noreply.git.ryujinx.app> Date: Sat, 21 Jun 2025 10:22:08 +0800 Subject: [PATCH 3/3] gdb: Show thread names Reference: https://github.com/Atmosphere-NX/Atmosphere/blob/d8a37b4b7184b80ba979bcceb98365b8365a1c3a/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_type.os.horizon.hpp --- src/Ryujinx.HLE/Debugger/Debugger.cs | 62 ++++++++++-- .../HOS/Kernel/Threading/KThread.cs | 95 +++++++++++++++++++ 2 files changed, 151 insertions(+), 6 deletions(-) diff --git a/src/Ryujinx.HLE/Debugger/Debugger.cs b/src/Ryujinx.HLE/Debugger/Debugger.cs index dd6394e60..9b9850e58 100644 --- a/src/Ryujinx.HLE/Debugger/Debugger.cs +++ b/src/Ryujinx.HLE/Debugger/Debugger.cs @@ -34,6 +34,8 @@ namespace Ryujinx.HLE.Debugger private ulong? cThread; private ulong? gThread; + private string previousThreadListXml = ""; + public Debugger(Switch device, ushort port) { Device = device; @@ -368,7 +370,7 @@ namespace Ryujinx.HLE.Debugger if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported")) { - Reply("PacketSize=10000;qXfer:features:read+"); + Reply("PacketSize=10000;qXfer:features:read+;qXfer:threads:read+"); break; } @@ -404,10 +406,43 @@ namespace Ryujinx.HLE.Debugger break; } + if (ss.ConsumePrefix("Xfer:threads:read:")) + { + ss.ReadUntil(':'); + ulong offset = ss.ReadUntilAsHex(','); + ulong len = ss.ReadRemainingAsHex(); + + var data = ""; + if (offset > 0) + { + data = previousThreadListXml; + } else + { + previousThreadListXml = data = GetThreadListXml(); + } + + if (offset >= (ulong)data.Length) + { + Reply("l"); + break; + } + + if (len >= (ulong)data.Length - offset) + { + Reply("l" + ToBinaryFormat(data.Substring((int)offset))); + break; + } + else + { + Reply("m" + ToBinaryFormat(data.Substring((int)offset, (int)len))); + break; + } + } + if (ss.ConsumePrefix("Xfer:features:read:")) { string feature = ss.ReadUntil(':'); - ulong addr = ss.ReadUntilAsHex(','); + ulong offset = ss.ReadUntilAsHex(','); ulong len = ss.ReadRemainingAsHex(); if (feature == "target.xml") @@ -418,20 +453,20 @@ namespace Ryujinx.HLE.Debugger string data; if (RegisterInformation.Features.TryGetValue(feature, out data)) { - if (addr >= (ulong)data.Length) + if (offset >= (ulong)data.Length) { Reply("l"); break; } - if (len >= (ulong)data.Length - addr) + if (len >= (ulong)data.Length - offset) { - Reply("l" + ToBinaryFormat(data.Substring((int)addr))); + Reply("l" + ToBinaryFormat(data.Substring((int)offset))); break; } else { - Reply("m" + ToBinaryFormat(data.Substring((int)addr, (int)len))); + Reply("m" + ToBinaryFormat(data.Substring((int)offset, (int)len))); break; } } @@ -469,6 +504,21 @@ namespace Ryujinx.HLE.Debugger } } + private string GetThreadListXml() + { + var sb = new StringBuilder(); + sb.Append("\n"); + + foreach (var thread in GetThreads()) + { + string threadName = System.Security.SecurityElement.Escape(thread.GetThreadName()); + sb.Append($"\n"); + } + + sb.Append(""); + return sb.ToString(); + } + void CommandQuery() { // GDB is performing initial contact. Stop everything. diff --git a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs index 2b5d11244..bb0548d19 100644 --- a/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs +++ b/src/Ryujinx.HLE/HOS/Kernel/Threading/KThread.cs @@ -5,9 +5,11 @@ using Ryujinx.HLE.HOS.Kernel.Common; using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.SupervisorCall; using Ryujinx.Horizon.Common; +using Ryujinx.Memory; using System; using System.Collections.Generic; using System.Numerics; +using System.Text; using System.Threading; namespace Ryujinx.HLE.HOS.Kernel.Threading @@ -17,6 +19,23 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading private const int TlsUserDisableCountOffset = 0x100; private const int TlsUserInterruptFlagOffset = 0x102; + // Tls -> ThreadType + private const int TlsThreadTypeOffsetAArch64 = 0x1F8; + private const int TlsThreadTypeOffsetAArch32 = 0x1FC; + + // Tls -> ThreadType -> Version + private const int TlsThreadTypeVersionOffsetAArch64 = 0x46; + private const int TlsThreadTypeVersionOffsetAArch32 = 0x26; + + // Tls -> ThreadType (Version 0) -> ThreadNamePointer + private const int TlsThreadTypeVersion0ThreadNamePointerOffsetAArch64 = 0x1A8; + private const int TlsThreadTypeVersion0ThreadNamePointerOffsetAArch32 = 0xE8; + + // Tls -> ThreadType (Version 1) -> ThreadNamePointer + private const int TlsThreadTypeThreadNamePointerOffsetAArch64 = 0x1A0; + private const int TlsThreadTypeThreadNamePointerOffsetAArch32 = 0xE4; + + public const int MaxWaitSyncObjects = 64; private ManualResetEvent _schedulerWaitEvent; @@ -1439,5 +1458,81 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading { Owner.CpuMemory.Write(_tlsAddress + TlsUserInterruptFlagOffset, 0); } + + public string GetThreadName() + { + try + { + ulong threadNamePtr = 0; + if (Context.IsAarch32) + { + uint threadTypePtr32 = Owner.CpuMemory.Read(_tlsAddress + TlsThreadTypeOffsetAArch32); + if (threadTypePtr32 == 0) + { + return ""; + } + + ushort version = Owner.CpuMemory.Read(threadTypePtr32 + TlsThreadTypeVersionOffsetAArch32); + switch (version) + { + case 0x0000: + case 0xFFFF: + threadNamePtr = Owner.CpuMemory.Read(threadTypePtr32 + TlsThreadTypeVersion0ThreadNamePointerOffsetAArch32); + break; + case 0x0001: + threadNamePtr = Owner.CpuMemory.Read(threadTypePtr32 + TlsThreadTypeThreadNamePointerOffsetAArch32); + break; + default: + Logger.Warning?.Print(LogClass.Kernel, $"Unknown ThreadType struct version: {version}"); + break; + } + } + else + { + ulong threadTypePtr64 = Owner.CpuMemory.Read(_tlsAddress + TlsThreadTypeOffsetAArch64); + if (threadTypePtr64 == 0) + { + return ""; + } + + ushort version = Owner.CpuMemory.Read(threadTypePtr64 + TlsThreadTypeVersionOffsetAArch64); + switch (version) + { + case 0x0000: + case 0xFFFF: + threadNamePtr = Owner.CpuMemory.Read(threadTypePtr64 + TlsThreadTypeVersion0ThreadNamePointerOffsetAArch64); + break; + case 0x0001: + threadNamePtr = Owner.CpuMemory.Read(threadTypePtr64 + TlsThreadTypeThreadNamePointerOffsetAArch64); + break; + default: + Logger.Warning?.Print(LogClass.Kernel, $"Unknown ThreadType struct version: {version}"); + break; + } + } + + if (threadNamePtr == 0) + { + return ""; + } + + List nameBytes = new(); + for (int i = 0; i < 0x20; i++) + { + byte b = Owner.CpuMemory.Read(threadNamePtr + (ulong)i); + if (b == 0) + { + break; + } + nameBytes.Add(b); + } + return Encoding.UTF8.GetString(nameBytes.ToArray()); + + } catch (InvalidMemoryRegionException) + { + Logger.Warning?.Print(LogClass.Kernel, "Failed to get thread name."); + return ""; + } + } } }