Debugger: Do more stuff

# Conflicts:
#	src/Ryujinx.HLE/Debugger/Debugger.cs
#	src/Ryujinx.HLE/Debugger/GdbXml/aarch64-core.xml
#	src/Ryujinx.HLE/Debugger/GdbXml/aarch64-fpu.xml
#	src/Ryujinx.HLE/Debugger/GdbXml/target.xml
#	src/Ryujinx.HLE/Debugger/RegisterInformation.cs
This commit is contained in:
merry 2022-02-09 21:29:53 +00:00 committed by svc64
parent 1bb8f6381c
commit a1de4f1b5b
7 changed files with 470 additions and 47 deletions

View file

@ -3,7 +3,6 @@ using Ryujinx.Common.Logging;
using Ryujinx.Memory;
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
@ -70,31 +69,31 @@ namespace Ryujinx.HLE.Debugger
switch (gdbRegId)
{
case >= 0 and <= 31:
return $"{state.GetX(gdbRegId):x16}";
return ToHex(BitConverter.GetBytes(state.GetX(gdbRegId)));
case 32:
return $"{state.DebugPc:x16}";
return ToHex(BitConverter.GetBytes(state.DebugPc));
case 33:
return $"{state.Pstate:x8}";
return ToHex(BitConverter.GetBytes(state.Pstate));
default:
throw new ArgumentException();
return null;
}
}
private void GdbWriteRegister(Ryujinx.Cpu.IExecutionContext state, int gdbRegId, ulong value)
private bool GdbWriteRegister(Ryujinx.Cpu.IExecutionContext state, int gdbRegId, ulong value)
{
switch (gdbRegId)
{
case >= 0 and <= 31:
state.SetX(gdbRegId, value);
return;
return true;
case 32:
state.DebugPc = value;
return;
return true;
case 33:
state.Pstate = (uint)value;
return;
return true;
default:
throw new ArgumentException();
return false;
}
}
@ -137,7 +136,7 @@ namespace Ryujinx.HLE.Debugger
goto unknownCommand;
}
// Enable extended mode
Reply("OK");
ReplyOK();
break;
case '?':
if (!ss.IsEmpty())
@ -205,29 +204,74 @@ namespace Ryujinx.HLE.Debugger
break;
}
case 'q':
switch (ss.ReadUntil(':'))
if (ss.ConsumeRemaining("GDBServerVersion"))
{
case "GDBServerVersion":
Reply($"name:Ryujinx;version:{ReleaseInformations.GetVersion()};");
Reply($"name:Ryujinx;version:{ReleaseInformation.GetVersion()};");
break;
case "HostInfo":
Reply($"triple:{ToHex("aarch64-none-elf")};endian:little;ptrsize:8;hostname:{ToHex("Ryujinx")};");
}
if (ss.ConsumeRemaining("HostInfo"))
{
Reply($"triple:{ToHex("aarch64-unknown-linux-android")};endian:little;ptrsize:8;hostname:{ToHex("Ryujinx")};");
break;
case "ProcessInfo":
Reply("pid:1;cputype:100000c;cpusubtype:0;ostype:unknown;vendor:none;endian:little;ptrsize:8;");
}
if (ss.ConsumeRemaining("ProcessInfo"))
{
Reply($"pid:1;cputype:100000c;cpusubtype:0;triple:{ToHex("aarch64-unknown-linux-android")};ostype:unknown;vendor:none;endian:little;ptrsize:8;");
break;
case "fThreadInfo":
Reply($"m {string.Join(",", GetThreadIds().Select(x => $"{x:x}"))}");
}
if (ss.ConsumePrefix("Supported:") || ss.ConsumeRemaining("Supported"))
{
Reply("PacketSize=10000,qXfer:features:read+");
break;
case "sThreadInfo":
}
if (ss.ConsumeRemaining("fThreadInfo"))
{
Reply($"m{string.Join(",", GetThreadIds().Select(x => $"{x:x}"))}");
break;
}
if (ss.ConsumeRemaining("sThreadInfo"))
{
Reply("l");
break;
default:
goto unknownCommand;
}
if (ss.ConsumePrefix("Xfer:features:read:"))
{
string feature = ss.ReadUntil(':');
ulong addr = ss.ReadUntilAsHex(',');
ulong len = ss.ReadRemainingAsHex();
string data;
if (RegisterInformation.Features.TryGetValue(feature, out data))
{
if (addr >= (ulong)data.Length)
{
Reply("l");
break;
}
if (len >= (ulong)data.Length - addr)
{
Reply("l" + data.Substring((int)addr));
break;
}
else
{
Reply("m" + data.Substring((int)addr, (int)len));
break;
}
}
else
{
Reply("E00"); // Invalid annex
break;
}
}
goto unknownCommand;
case 'Q':
goto unknownCommand;
default:
Logger.Notice.Print(LogClass.GdbStub, $"Unknown command: {cmd}");
unknownCommand:
// Logger.Notice.Print(LogClass.GdbStub, $"Unknown command: {cmd}");
Reply("");
break;
}
@ -276,9 +320,16 @@ namespace Ryujinx.HLE.Debugger
var ctx = GetThread(gThread);
for (int i = 0; i < GdbRegisterCount; i++)
{
GdbWriteRegister(ctx, i, ss.ReadLengthAsHex(GdbRegisterHexSize(i)));
GdbWriteRegister(ctx, i, ss.ReadLengthAsLEHex(GdbRegisterHexSize(i)));
}
if (ss.IsEmpty())
{
ReplyOK();
}
else
{
ReplyError();
}
Reply(ss.IsEmpty() ? "OK" : "E99");
}
void CommandSetThread(char op, ulong threadId)
@ -287,26 +338,35 @@ namespace Ryujinx.HLE.Debugger
{
case 'c':
cThread = threadId;
Reply("OK");
ReplyOK();
return;
case 'g':
gThread = threadId;
Reply("OK");
ReplyOK();
return;
default:
Reply("E99");
ReplyError();
return;
}
}
void CommandReadMemory(ulong addr, ulong len)
{
try
{
var data = new byte[len];
GetMemory().Read(addr, data);
Reply(ToHex(data));
}
catch (InvalidMemoryRegionException)
{
ReplyError();
}
}
void CommandWriteMemory(ulong addr, ulong len, StringStream ss)
{
try
{
var data = new byte[len];
for (ulong i = 0; i < len; i++)
@ -314,26 +374,57 @@ namespace Ryujinx.HLE.Debugger
data[i] = (byte)ss.ReadLengthAsHex(2);
}
GetMemory().Write(addr, data);
ReplyOK();
}
catch (InvalidMemoryRegionException)
{
ReplyError();
}
}
void CommandReadGeneralRegister(int gdbRegId)
{
var ctx = GetThread(gThread);
Reply(GdbReadRegister(ctx, gdbRegId));
string result = GdbReadRegister(ctx, gdbRegId);
if (result != null)
{
Reply(result);
}
else
{
ReplyError();
}
}
void CommandWriteGeneralRegister(int gdbRegId, ulong value)
{
var ctx = GetThread(gThread);
GdbWriteRegister(ctx, gdbRegId, value);
Reply("OK");
if (GdbWriteRegister(ctx, gdbRegId, value))
{
ReplyOK();
}
else
{
ReplyError();
}
}
private void Reply(string cmd)
{
Logger.Notice.Print(LogClass.GdbStub, $"Reply: {cmd}");
WriteStream.Write(Encoding.ASCII.GetBytes($"${cmd}#{CalculateChecksum(cmd):x2}"));
}
private void ReplyOK()
{
Reply("OK");
}
private void ReplyError()
{
Reply("E01");
}
private void SocketReaderThreadMain()
{
restartListen:
@ -345,6 +436,7 @@ namespace Ryujinx.HLE.Debugger
Logger.Notice.Print(LogClass.GdbStub, $"Currently waiting on {endpoint} for GDB client");
ClientSocket = ListenerSocket.AcceptSocket();
ClientSocket.NoDelay = true;
ReadStream = new NetworkStream(ClientSocket, System.IO.FileAccess.Read);
WriteStream = new NetworkStream(ClientSocket, System.IO.FileAccess.Write);
Logger.Notice.Print(LogClass.GdbStub, "GDB client connected");

View file

@ -0,0 +1,93 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2022 Free Software Foundation, Inc.
Contributed by ARM Ltd.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.aarch64.core">
<reg name="x0" bitsize="64"/>
<reg name="x1" bitsize="64"/>
<reg name="x2" bitsize="64"/>
<reg name="x3" bitsize="64"/>
<reg name="x4" bitsize="64"/>
<reg name="x5" bitsize="64"/>
<reg name="x6" bitsize="64"/>
<reg name="x7" bitsize="64"/>
<reg name="x8" bitsize="64"/>
<reg name="x9" bitsize="64"/>
<reg name="x10" bitsize="64"/>
<reg name="x11" bitsize="64"/>
<reg name="x12" bitsize="64"/>
<reg name="x13" bitsize="64"/>
<reg name="x14" bitsize="64"/>
<reg name="x15" bitsize="64"/>
<reg name="x16" bitsize="64"/>
<reg name="x17" bitsize="64"/>
<reg name="x18" bitsize="64"/>
<reg name="x19" bitsize="64"/>
<reg name="x20" bitsize="64"/>
<reg name="x21" bitsize="64"/>
<reg name="x22" bitsize="64"/>
<reg name="x23" bitsize="64"/>
<reg name="x24" bitsize="64"/>
<reg name="x25" bitsize="64"/>
<reg name="x26" bitsize="64"/>
<reg name="x27" bitsize="64"/>
<reg name="x28" bitsize="64"/>
<reg name="x29" bitsize="64"/>
<reg name="x30" bitsize="64"/>
<reg name="sp" bitsize="64" type="data_ptr"/>
<reg name="pc" bitsize="64" type="code_ptr"/>
<flags id="cpsr_flags" size="4">
<!-- Stack Pointer. -->
<field name="SP" start="0" end="0"/>
<!-- Exception Level. -->
<field name="EL" start="2" end="3"/>
<!-- Execution state. -->
<field name="nRW" start="4" end="4"/>
<!-- FIQ interrupt mask. -->
<field name="F" start="6" end="6"/>
<!-- IRQ interrupt mask. -->
<field name="I" start="7" end="7"/>
<!-- SError interrupt mask. -->
<field name="A" start="8" end="8"/>
<!-- Debug exception mask. -->
<field name="D" start="9" end="9"/>
<!-- ARMv8.5-A: Branch Target Identification BTYPE. -->
<field name="BTYPE" start="10" end="11"/>
<!-- ARMv8.0-A: Speculative Store Bypass. -->
<field name="SSBS" start="12" end="12"/>
<!-- Illegal Execution state. -->
<field name="IL" start="20" end="20"/>
<!-- Software Step. -->
<field name="SS" start="21" end="21"/>
<!-- ARMv8.1-A: Privileged Access Never. -->
<field name="PAN" start="22" end="22"/>
<!-- ARMv8.2-A: User Access Override. -->
<field name="UAO" start="23" end="23"/>
<!-- ARMv8.4-A: Data Independent Timing. -->
<field name="DIT" start="24" end="24"/>
<!-- ARMv8.5-A: Tag Check Override. -->
<field name="TCO" start="25" end="25"/>
<!-- Overflow Condition flag. -->
<field name="V" start="28" end="28"/>
<!-- Carry Condition flag. -->
<field name="C" start="29" end="29"/>
<!-- Zero Condition flag. -->
<field name="Z" start="30" end="30"/>
<!-- Negative Condition flag. -->
<field name="N" start="31" end="31"/>
</flags>
<reg name="cpsr" bitsize="32" type="cpsr_flags"/>
</feature>

View file

@ -0,0 +1,159 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2022 Free Software Foundation, Inc.
Contributed by ARM Ltd.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.aarch64.fpu">
<vector id="v2d" type="ieee_double" count="2"/>
<vector id="v2u" type="uint64" count="2"/>
<vector id="v2i" type="int64" count="2"/>
<vector id="v4f" type="ieee_single" count="4"/>
<vector id="v4u" type="uint32" count="4"/>
<vector id="v4i" type="int32" count="4"/>
<vector id="v8f" type="ieee_half" count="8"/>
<vector id="v8u" type="uint16" count="8"/>
<vector id="v8i" type="int16" count="8"/>
<vector id="v8bf16" type="bfloat16" count="8"/>
<vector id="v16u" type="uint8" count="16"/>
<vector id="v16i" type="int8" count="16"/>
<vector id="v1u" type="uint128" count="1"/>
<vector id="v1i" type="int128" count="1"/>
<union id="vnd">
<field name="f" type="v2d"/>
<field name="u" type="v2u"/>
<field name="s" type="v2i"/>
</union>
<union id="vns">
<field name="f" type="v4f"/>
<field name="u" type="v4u"/>
<field name="s" type="v4i"/>
</union>
<union id="vnh">
<field name="bf" type="v8bf16"/>
<field name="f" type="v8f"/>
<field name="u" type="v8u"/>
<field name="s" type="v8i"/>
</union>
<union id="vnb">
<field name="u" type="v16u"/>
<field name="s" type="v16i"/>
</union>
<union id="vnq">
<field name="u" type="v1u"/>
<field name="s" type="v1i"/>
</union>
<union id="aarch64v">
<field name="d" type="vnd"/>
<field name="s" type="vns"/>
<field name="h" type="vnh"/>
<field name="b" type="vnb"/>
<field name="q" type="vnq"/>
</union>
<reg name="v0" bitsize="128" type="aarch64v" regnum="34"/>
<reg name="v1" bitsize="128" type="aarch64v" />
<reg name="v2" bitsize="128" type="aarch64v" />
<reg name="v3" bitsize="128" type="aarch64v" />
<reg name="v4" bitsize="128" type="aarch64v" />
<reg name="v5" bitsize="128" type="aarch64v" />
<reg name="v6" bitsize="128" type="aarch64v" />
<reg name="v7" bitsize="128" type="aarch64v" />
<reg name="v8" bitsize="128" type="aarch64v" />
<reg name="v9" bitsize="128" type="aarch64v" />
<reg name="v10" bitsize="128" type="aarch64v"/>
<reg name="v11" bitsize="128" type="aarch64v"/>
<reg name="v12" bitsize="128" type="aarch64v"/>
<reg name="v13" bitsize="128" type="aarch64v"/>
<reg name="v14" bitsize="128" type="aarch64v"/>
<reg name="v15" bitsize="128" type="aarch64v"/>
<reg name="v16" bitsize="128" type="aarch64v"/>
<reg name="v17" bitsize="128" type="aarch64v"/>
<reg name="v18" bitsize="128" type="aarch64v"/>
<reg name="v19" bitsize="128" type="aarch64v"/>
<reg name="v20" bitsize="128" type="aarch64v"/>
<reg name="v21" bitsize="128" type="aarch64v"/>
<reg name="v22" bitsize="128" type="aarch64v"/>
<reg name="v23" bitsize="128" type="aarch64v"/>
<reg name="v24" bitsize="128" type="aarch64v"/>
<reg name="v25" bitsize="128" type="aarch64v"/>
<reg name="v26" bitsize="128" type="aarch64v"/>
<reg name="v27" bitsize="128" type="aarch64v"/>
<reg name="v28" bitsize="128" type="aarch64v"/>
<reg name="v29" bitsize="128" type="aarch64v"/>
<reg name="v30" bitsize="128" type="aarch64v"/>
<reg name="v31" bitsize="128" type="aarch64v"/>
<flags id="fpsr_flags" size="4">
<!-- Invalid Operation cumulative floating-point exception bit. -->
<field name="IOC" start="0" end="0"/>
<!-- Divide by Zero cumulative floating-point exception bit. -->
<field name="DZC" start="1" end="1"/>
<!-- Overflow cumulative floating-point exception bit. -->
<field name="OFC" start="2" end="2"/>
<!-- Underflow cumulative floating-point exception bit. -->
<field name="UFC" start="3" end="3"/>
<!-- Inexact cumulative floating-point exception bit.. -->
<field name="IXC" start="4" end="4"/>
<!-- Input Denormal cumulative floating-point exception bit. -->
<field name="IDC" start="7" end="7"/>
<!-- Cumulative saturation bit, Advanced SIMD only. -->
<field name="QC" start="27" end="27"/>
<!-- When AArch32 is supported at any Exception level and AArch32
floating-point is implemented: Overflow condition flag for AArch32
floating-point comparison operations. -->
<field name="V" start="28" end="28"/>
<!-- When AArch32 is supported at any Exception level and AArch32
floating-point is implemented:
Carry condition flag for AArch32 floating-point comparison operations.
-->
<field name="C" start="29" end="29"/>
<!-- When AArch32 is supported at any Exception level and AArch32
floating-point is implemented:
Zero condition flag for AArch32 floating-point comparison operations.
-->
<field name="Z" start="30" end="30"/>
<!-- When AArch32 is supported at any Exception level and AArch32
floating-point is implemented:
Negative condition flag for AArch32 floating-point comparison
operations. -->
<field name="N" start="31" end="31"/>
</flags>
<reg name="fpsr" bitsize="32" type="fpsr_flags"/>
<flags id="fpcr_flags" size="4">
<!-- Flush Inputs to Zero (part of Armv8.7). -->
<field name="FIZ" start="0" end="0"/>
<!-- Alternate Handling (part of Armv8.7). -->
<field name="AH" start="1" end="1"/>
<!-- Controls how the output elements other than the lowest element of the
vector are determined for Advanced SIMD scalar instructions (part of
Armv8.7). -->
<field name="NEP" start="2" end="2"/>
<!-- Invalid Operation floating-point exception trap enable. -->
<field name="IOE" start="8" end="8"/>
<!-- Divide by Zero floating-point exception trap enable. -->
<field name="DZE" start="9" end="9"/>
<!-- Overflow floating-point exception trap enable. -->
<field name="OFE" start="10" end="10"/>
<!-- Underflow floating-point exception trap enable. -->
<field name="UFE" start="11" end="11"/>
<!-- Inexact floating-point exception trap enable. -->
<field name="IXE" start="12" end="12"/>
<!-- Input Denormal floating-point exception trap enable. -->
<field name="IDE" start="15" end="15"/>
<!-- Flush-to-zero mode control bit on half-precision data-processing
instructions. -->
<field name="FZ16" start="19" end="19"/>
<!-- Rounding Mode control field. -->
<field name="RMode" start="22" end="23"/>
<!-- Flush-to-zero mode control bit. -->
<field name="FZ" start="24" end="24"/>
<!-- Default NaN mode control bit. -->
<field name="DN" start="25" end="25"/>
<!-- Alternative half-precision control bit. -->
<field name="AHP" start="26" end="26"/>
</flags>
<reg name="fpcr" bitsize="32" type="fpcr_flags"/>
</feature>

View file

@ -0,0 +1,14 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2009-2013 Free Software Foundation, Inc.
Contributed by ARM Ltd.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>aarch64</architecture>
<xi:include href="aarch64-core.xml"/>
<xi:include href="aarch64-fpu.xml"/>
</target>

View file

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.IO;
namespace Ryujinx.HLE.Debugger
{
class RegisterInformation
{
public static readonly Dictionary<string, string> Features = new()
{
{ "target.xml", GetEmbeddedResourceContent("target.xml") },
{ "aarch64-core.xml", GetEmbeddedResourceContent("aarch64-core.xml") },
{ "aarch64-fpu.xml", GetEmbeddedResourceContent("aarch64-fpu.xml") },
};
private static string GetEmbeddedResourceContent(string resourceName)
{
Stream stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Ryujinx.HLE.Debugger.GdbXml." + resourceName);
StreamReader reader = new StreamReader(stream);
string result = reader.ReadToEnd();
reader.Dispose();
stream.Dispose();
return result;
}
}
}

View file

@ -1,4 +1,5 @@
using System.Globalization;
using System.Diagnostics;
using System.Globalization;
namespace Ryujinx.HLE.Debugger
{
@ -60,6 +61,39 @@ namespace Ryujinx.HLE.Debugger
return ulong.Parse(ReadLength(len), NumberStyles.HexNumber);
}
public ulong ReadLengthAsLEHex(int len)
{
Debug.Assert(len % 2 == 0);
ulong result = 0;
int pos = 0;
while (pos < len)
{
result += ReadLengthAsHex(2) << (4 * pos);
pos += 2;
}
return result;
}
public bool ConsumePrefix(string prefix)
{
if (Data.Substring(Position).StartsWith(prefix))
{
Position += prefix.Length;
return true;
}
return false;
}
public bool ConsumeRemaining(string match)
{
if (Data.Substring(Position) == match)
{
Position += match.Length;
return true;
}
return false;
}
public bool IsEmpty()
{
return Position >= Data.Length;

View file

@ -30,6 +30,9 @@
</ItemGroup>
<ItemGroup>
<None Remove="Debugger\GdbXml\aarch64-core.xml" />
<None Remove="Debugger\GdbXml\aarch64-fpu.xml" />
<None Remove="Debugger\GdbXml\target.xml" />
<None Remove="Homebrew.npdm" />
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Logo_Ryujinx.png" />
<None Remove="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnA.png" />
@ -39,6 +42,9 @@
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Debugger\GdbXml\aarch64-core.xml" />
<EmbeddedResource Include="Debugger\GdbXml\aarch64-fpu.xml" />
<EmbeddedResource Include="Debugger\GdbXml\target.xml" />
<EmbeddedResource Include="Homebrew.npdm" />
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Logo_Ryujinx.png" />
<EmbeddedResource Include="HOS\Applets\SoftwareKeyboard\Resources\Icon_BtnA.png" />