mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-06-27 22:06:24 +02:00
gdb: Implement QRcmd (monitor) commands
monitor backtrace (mo bt) monitor registers (mo reg) monitor get info
This commit is contained in:
parent
7d189ab2c0
commit
009d319bc2
4 changed files with 144 additions and 4 deletions
|
@ -2,6 +2,7 @@ using ARMeilleure.State;
|
||||||
using Ryujinx.Common;
|
using Ryujinx.Common;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
using Ryujinx.HLE.HOS.Kernel;
|
using Ryujinx.HLE.HOS.Kernel;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Process;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
using System;
|
using System;
|
||||||
|
@ -53,7 +54,8 @@ namespace Ryujinx.HLE.Debugger
|
||||||
BreakpointManager = new BreakpointManager(this);
|
BreakpointManager = new BreakpointManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDebuggableProcess DebugProcess => Device.System?.DebugGetApplicationProcess();
|
internal KProcess Process => Device.System?.DebugGetApplicationProcess();
|
||||||
|
internal IDebuggableProcess DebugProcess => Device.System?.DebugGetApplicationProcessDebugInterface();
|
||||||
private KThread[] GetThreads() => DebugProcess.GetThreadUids().Select(x => DebugProcess.GetThread(x)).ToArray();
|
private KThread[] GetThreads() => DebugProcess.GetThreadUids().Select(x => DebugProcess.GetThread(x)).ToArray();
|
||||||
internal bool IsProcessAarch32 => DebugProcess.GetThread(gThread.Value).Context.IsAarch32;
|
internal bool IsProcessAarch32 => DebugProcess.GetThread(gThread.Value).Context.IsAarch32;
|
||||||
private KernelContext KernelContext => Device.System.KernelContext;
|
private KernelContext KernelContext => Device.System.KernelContext;
|
||||||
|
@ -379,6 +381,13 @@ namespace Ryujinx.HLE.Debugger
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ss.ConsumePrefix("Rcmd,"))
|
||||||
|
{
|
||||||
|
string hexCommand = ss.ReadRemaining();
|
||||||
|
HandleQRcmdCommand(hexCommand);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
if (ss.ConsumeRemaining("fThreadInfo"))
|
if (ss.ConsumeRemaining("fThreadInfo"))
|
||||||
{
|
{
|
||||||
Reply($"m{string.Join(",", DebugProcess.GetThreadUids().Select(x => $"{x:x}"))}");
|
Reply($"m{string.Join(",", DebugProcess.GetThreadUids().Select(x => $"{x:x}"))}");
|
||||||
|
@ -982,6 +991,97 @@ namespace Ryujinx.HLE.Debugger
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleQRcmdCommand(string hexCommand)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
string command = FromHex(hexCommand);
|
||||||
|
Logger.Debug?.Print(LogClass.GdbStub, $"Received Rcmd: {command}");
|
||||||
|
|
||||||
|
string response = command.Trim().ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"help" => "backtrace\nbt\nregisters\nreg\nget info\n",
|
||||||
|
"get info" => GetProcessInfo(),
|
||||||
|
"backtrace" => GetStackTrace(),
|
||||||
|
"bt" => GetStackTrace(),
|
||||||
|
"registers" => GetRegisters(),
|
||||||
|
"reg" => GetRegisters(),
|
||||||
|
_ => $"Unknown command: {command}\n"
|
||||||
|
};
|
||||||
|
|
||||||
|
Reply(ToHex(response));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.GdbStub, $"Error processing Rcmd: {e.Message}");
|
||||||
|
ReplyError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetStackTrace()
|
||||||
|
{
|
||||||
|
if (gThread == null)
|
||||||
|
return "No thread selected\n";
|
||||||
|
|
||||||
|
if (Process == null)
|
||||||
|
return "No application process found\n";
|
||||||
|
|
||||||
|
return Process.Debugger.GetGuestStackTrace(DebugProcess.GetThread(gThread.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetRegisters()
|
||||||
|
{
|
||||||
|
if (gThread == null)
|
||||||
|
return "No thread selected\n";
|
||||||
|
|
||||||
|
if (Process == null)
|
||||||
|
return "No application process found\n";
|
||||||
|
|
||||||
|
return Process.Debugger.GetCpuRegisterPrintout(DebugProcess.GetThread(gThread.Value));
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetProcessInfo()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (Process == null)
|
||||||
|
return "No application process found\n";
|
||||||
|
|
||||||
|
KProcess kProcess = Process;
|
||||||
|
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
|
||||||
|
sb.AppendLine($"Program Id: 0x{kProcess.TitleId:x16}");
|
||||||
|
sb.AppendLine($"Application: {(kProcess.IsApplication ? 1 : 0)}");
|
||||||
|
sb.AppendLine("Layout:");
|
||||||
|
sb.AppendLine($" Alias: 0x{kProcess.MemoryManager.AliasRegionStart:x10} - 0x{kProcess.MemoryManager.AliasRegionEnd - 1:x10}");
|
||||||
|
sb.AppendLine($" Heap: 0x{kProcess.MemoryManager.HeapRegionStart:x10} - 0x{kProcess.MemoryManager.HeapRegionEnd - 1:x10}");
|
||||||
|
sb.AppendLine($" Aslr: 0x{kProcess.MemoryManager.AslrRegionStart:x10} - 0x{kProcess.MemoryManager.AslrRegionEnd - 1:x10}");
|
||||||
|
sb.AppendLine($" Stack: 0x{kProcess.MemoryManager.StackRegionStart:x10} - 0x{kProcess.MemoryManager.StackRegionEnd - 1:x10}");
|
||||||
|
|
||||||
|
sb.AppendLine("Modules:");
|
||||||
|
var debugger = kProcess.Debugger;
|
||||||
|
if (debugger != null)
|
||||||
|
{
|
||||||
|
var images = debugger.GetLoadedImages();
|
||||||
|
for (int i = 0; i < images.Count; i++)
|
||||||
|
{
|
||||||
|
var image = images[i];
|
||||||
|
ulong endAddress = image.BaseAddress + image.Size - 1;
|
||||||
|
string name = debugger.GetGuessedNsoNameFromIndex(i);
|
||||||
|
sb.AppendLine($" 0x{image.BaseAddress:x10} - 0x{endAddress:x10} {name}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Logger.Error?.Print(LogClass.GdbStub, $"Error getting process info: {e.Message}");
|
||||||
|
return $"Error getting process info: {e.Message}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void Reply(string cmd)
|
private void Reply(string cmd)
|
||||||
{
|
{
|
||||||
Logger.Debug?.Print(LogClass.GdbStub, $"Reply: {cmd}");
|
Logger.Debug?.Print(LogClass.GdbStub, $"Reply: {cmd}");
|
||||||
|
@ -1108,6 +1208,15 @@ namespace Ryujinx.HLE.Debugger
|
||||||
return checksum;
|
return checksum;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string FromHex(string hexString)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(hexString))
|
||||||
|
return string.Empty;
|
||||||
|
|
||||||
|
byte[] bytes = Convert.FromHexString(hexString);
|
||||||
|
return Encoding.ASCII.GetString(bytes);
|
||||||
|
}
|
||||||
|
|
||||||
private string ToHex(byte[] bytes)
|
private string ToHex(byte[] bytes)
|
||||||
{
|
{
|
||||||
return string.Join("", bytes.Select(x => $"{x:x2}"));
|
return string.Join("", bytes.Select(x => $"{x:x2}"));
|
||||||
|
|
|
@ -502,12 +502,20 @@ namespace Ryujinx.HLE.HOS
|
||||||
IsPaused = pause;
|
IsPaused = pause;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal IDebuggableProcess DebugGetApplicationProcess()
|
internal IDebuggableProcess DebugGetApplicationProcessDebugInterface()
|
||||||
{
|
{
|
||||||
lock (KernelContext.Processes)
|
lock (KernelContext.Processes)
|
||||||
{
|
{
|
||||||
return KernelContext.Processes.Values.FirstOrDefault(x => x.IsApplication)?.DebugInterface;
|
return KernelContext.Processes.Values.FirstOrDefault(x => x.IsApplication)?.DebugInterface;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal KProcess DebugGetApplicationProcess()
|
||||||
|
{
|
||||||
|
lock (KernelContext.Processes)
|
||||||
|
{
|
||||||
|
return KernelContext.Processes.Values.FirstOrDefault(x => x.IsApplication);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using Ryujinx.HLE.HOS.Kernel.Threading;
|
using Ryujinx.HLE.HOS.Kernel.Threading;
|
||||||
using Ryujinx.HLE.Loaders.Elf;
|
using Ryujinx.HLE.Loaders.Elf;
|
||||||
using Ryujinx.Memory;
|
using Ryujinx.Memory;
|
||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
@ -17,7 +18,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
private readonly KProcess _owner;
|
private readonly KProcess _owner;
|
||||||
|
|
||||||
private class Image
|
public class Image
|
||||||
{
|
{
|
||||||
public ulong BaseAddress { get; }
|
public ulong BaseAddress { get; }
|
||||||
public ulong Size { get; }
|
public ulong Size { get; }
|
||||||
|
@ -54,6 +55,15 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
|
|
||||||
trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}");
|
trace.AppendLine($"Process: {_owner.Name}, PID: {_owner.Pid}");
|
||||||
|
|
||||||
|
string ThreadName = thread.GetThreadName();
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(ThreadName))
|
||||||
|
{
|
||||||
|
trace.AppendLine($"Thread ID: {thread.ThreadUid} ({ThreadName})");
|
||||||
|
} else {
|
||||||
|
trace.AppendLine($"Thread ID: {thread.ThreadUid}");
|
||||||
|
}
|
||||||
|
|
||||||
void AppendTrace(ulong address)
|
void AppendTrace(ulong address)
|
||||||
{
|
{
|
||||||
if (AnalyzePointer(out PointerInfo info, address, thread))
|
if (AnalyzePointer(out PointerInfo info, address, thread))
|
||||||
|
@ -283,7 +293,7 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetGuessedNsoNameFromIndex(int index)
|
public string GetGuessedNsoNameFromIndex(int index)
|
||||||
{
|
{
|
||||||
if ((uint)index > 11)
|
if ((uint)index > 11)
|
||||||
{
|
{
|
||||||
|
@ -316,6 +326,16 @@ namespace Ryujinx.HLE.HOS.Kernel.Process
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Image> GetLoadedImages()
|
||||||
|
{
|
||||||
|
EnsureLoaded();
|
||||||
|
|
||||||
|
lock (_images)
|
||||||
|
{
|
||||||
|
return [.. _images];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void EnsureLoaded()
|
private void EnsureLoaded()
|
||||||
{
|
{
|
||||||
if (Interlocked.CompareExchange(ref _loaded, 1, 0) == 0)
|
if (Interlocked.CompareExchange(ref _loaded, 1, 0) == 0)
|
||||||
|
|
|
@ -1532,6 +1532,9 @@ namespace Ryujinx.HLE.HOS.Kernel.Threading
|
||||||
{
|
{
|
||||||
Logger.Warning?.Print(LogClass.Kernel, "Failed to get thread name.");
|
Logger.Warning?.Print(LogClass.Kernel, "Failed to get thread name.");
|
||||||
return "";
|
return "";
|
||||||
|
} catch (Exception e) {
|
||||||
|
Logger.Error?.Print(LogClass.Kernel, $"Error getting thread name: {e.Message}");
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue