gdb: Show thread names

Reference: d8a37b4b71/libraries/libstratosphere/source/osdbg/impl/osdbg_thread_type.os.horizon.hpp
This commit is contained in:
Coxxs 2025-06-21 10:22:08 +08:00
parent 9552cafaaa
commit 92d8f0eb1c
2 changed files with 151 additions and 6 deletions

View file

@ -34,6 +34,8 @@ namespace Ryujinx.HLE.Debugger
private ulong? cThread; private ulong? cThread;
private ulong? gThread; private ulong? gThread;
private string previousThreadListXml = "";
public Debugger(Switch device, ushort port) public Debugger(Switch device, ushort port)
{ {
Device = device; Device = device;
@ -368,7 +370,7 @@ namespace Ryujinx.HLE.Debugger
if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported")) if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported"))
{ {
Reply("PacketSize=10000;qXfer:features:read+"); Reply("PacketSize=10000;qXfer:features:read+;qXfer:threads:read+");
break; break;
} }
@ -404,10 +406,43 @@ namespace Ryujinx.HLE.Debugger
break; 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:")) if (ss.ConsumePrefix("Xfer:features:read:"))
{ {
string feature = ss.ReadUntil(':'); string feature = ss.ReadUntil(':');
ulong addr = ss.ReadUntilAsHex(','); ulong offset = ss.ReadUntilAsHex(',');
ulong len = ss.ReadRemainingAsHex(); ulong len = ss.ReadRemainingAsHex();
if (feature == "target.xml") if (feature == "target.xml")
@ -418,20 +453,20 @@ namespace Ryujinx.HLE.Debugger
string data; string data;
if (RegisterInformation.Features.TryGetValue(feature, out data)) if (RegisterInformation.Features.TryGetValue(feature, out data))
{ {
if (addr >= (ulong)data.Length) if (offset >= (ulong)data.Length)
{ {
Reply("l"); Reply("l");
break; 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; break;
} }
else else
{ {
Reply("m" + ToBinaryFormat(data.Substring((int)addr, (int)len))); Reply("m" + ToBinaryFormat(data.Substring((int)offset, (int)len)));
break; break;
} }
} }
@ -469,6 +504,21 @@ namespace Ryujinx.HLE.Debugger
} }
} }
private string GetThreadListXml()
{
var sb = new StringBuilder();
sb.Append("<?xml version=\"1.0\"?><threads>\n");
foreach (var thread in GetThreads())
{
string threadName = System.Security.SecurityElement.Escape(thread.GetThreadName());
sb.Append($"<thread id=\"{thread.ThreadUid:x}\" name=\"{threadName}\" />\n");
}
sb.Append("</threads>");
return sb.ToString();
}
void CommandQuery() void CommandQuery()
{ {
// GDB is performing initial contact. Stop everything. // GDB is performing initial contact. Stop everything.

View file

@ -5,9 +5,11 @@ using Ryujinx.HLE.HOS.Kernel.Common;
using Ryujinx.HLE.HOS.Kernel.Process; using Ryujinx.HLE.HOS.Kernel.Process;
using Ryujinx.HLE.HOS.Kernel.SupervisorCall; using Ryujinx.HLE.HOS.Kernel.SupervisorCall;
using Ryujinx.Horizon.Common; using Ryujinx.Horizon.Common;
using Ryujinx.Memory;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Numerics; using System.Numerics;
using System.Text;
using System.Threading; using System.Threading;
namespace Ryujinx.HLE.HOS.Kernel.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 TlsUserDisableCountOffset = 0x100;
private const int TlsUserInterruptFlagOffset = 0x102; 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; public const int MaxWaitSyncObjects = 64;
private ManualResetEvent _schedulerWaitEvent; private ManualResetEvent _schedulerWaitEvent;
@ -1439,5 +1458,81 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
{ {
Owner.CpuMemory.Write<ushort>(_tlsAddress + TlsUserInterruptFlagOffset, 0); Owner.CpuMemory.Write<ushort>(_tlsAddress + TlsUserInterruptFlagOffset, 0);
} }
public string GetThreadName()
{
try
{
ulong threadNamePtr = 0;
if (Context.IsAarch32)
{
uint threadTypePtr32 = Owner.CpuMemory.Read<uint>(_tlsAddress + TlsThreadTypeOffsetAArch32);
if (threadTypePtr32 == 0)
{
return "";
}
ushort version = Owner.CpuMemory.Read<ushort>(threadTypePtr32 + TlsThreadTypeVersionOffsetAArch32);
switch (version)
{
case 0x0000:
case 0xFFFF:
threadNamePtr = Owner.CpuMemory.Read<uint>(threadTypePtr32 + TlsThreadTypeVersion0ThreadNamePointerOffsetAArch32);
break;
case 0x0001:
threadNamePtr = Owner.CpuMemory.Read<uint>(threadTypePtr32 + TlsThreadTypeThreadNamePointerOffsetAArch32);
break;
default:
Logger.Warning?.Print(LogClass.Kernel, $"Unknown ThreadType struct version: {version}");
break;
}
}
else
{
ulong threadTypePtr64 = Owner.CpuMemory.Read<ulong>(_tlsAddress + TlsThreadTypeOffsetAArch64);
if (threadTypePtr64 == 0)
{
return "";
}
ushort version = Owner.CpuMemory.Read<ushort>(threadTypePtr64 + TlsThreadTypeVersionOffsetAArch64);
switch (version)
{
case 0x0000:
case 0xFFFF:
threadNamePtr = Owner.CpuMemory.Read<ulong>(threadTypePtr64 + TlsThreadTypeVersion0ThreadNamePointerOffsetAArch64);
break;
case 0x0001:
threadNamePtr = Owner.CpuMemory.Read<ulong>(threadTypePtr64 + TlsThreadTypeThreadNamePointerOffsetAArch64);
break;
default:
Logger.Warning?.Print(LogClass.Kernel, $"Unknown ThreadType struct version: {version}");
break;
}
}
if (threadNamePtr == 0)
{
return "";
}
List<byte> nameBytes = new();
for (int i = 0; i < 0x20; i++)
{
byte b = Owner.CpuMemory.Read<byte>(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 "";
}
}
} }
} }