mirror of
https://git.ryujinx.app/ryubing/ryujinx.git
synced 2025-12-12 22:36:59 +00:00
Merge branch 'gdb-memory-mapping' into 'master'
gdb: add `monitor get mapping` See merge request [ryubing/ryujinx!215](https://git.ryujinx.app/ryubing/ryujinx/-/merge_requests/215)
This commit is contained in:
commit
50fb918326
2 changed files with 244 additions and 21 deletions
|
|
@ -1,43 +1,91 @@
|
||||||
using Gommon;
|
using Gommon;
|
||||||
using Ryujinx.Common.Logging;
|
using Ryujinx.Common.Logging;
|
||||||
|
using Ryujinx.HLE.HOS.Kernel.Memory;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
namespace Ryujinx.HLE.Debugger
|
namespace Ryujinx.HLE.Debugger
|
||||||
{
|
{
|
||||||
public partial class Debugger
|
public partial class Debugger
|
||||||
{
|
{
|
||||||
|
private sealed record RcmdEntry(string[] Names, Func<Debugger, string, string> Handler, string[] HelpLines);
|
||||||
|
|
||||||
|
// Atmosphere/libraries/libmesosphere/source/kern_k_memory_block_manager.cpp
|
||||||
|
private static readonly string[] _memoryStateNames =
|
||||||
|
{
|
||||||
|
"----- Free -----",
|
||||||
|
"Io ",
|
||||||
|
"Static ",
|
||||||
|
"Code ",
|
||||||
|
"CodeData ",
|
||||||
|
"Normal ",
|
||||||
|
"Shared ",
|
||||||
|
"Alias ",
|
||||||
|
"AliasCode ",
|
||||||
|
"AliasCodeData ",
|
||||||
|
"Ipc ",
|
||||||
|
"Stack ",
|
||||||
|
"ThreadLocal ",
|
||||||
|
"Transfered ",
|
||||||
|
"SharedTransfered",
|
||||||
|
"SharedCode ",
|
||||||
|
"Inaccessible ",
|
||||||
|
"NonSecureIpc ",
|
||||||
|
"NonDeviceIpc ",
|
||||||
|
"Kernel ",
|
||||||
|
"GeneratedCode ",
|
||||||
|
"CodeOut ",
|
||||||
|
"Coverage ",
|
||||||
|
};
|
||||||
|
|
||||||
static Debugger()
|
static Debugger()
|
||||||
{
|
{
|
||||||
_rcmdDelegates.Add(["help"],
|
_rcmdDelegates.Add(new RcmdEntry(
|
||||||
_ => _rcmdDelegates.Keys
|
["help"],
|
||||||
.Where(x => !x[0].Equals("help"))
|
(dbgr, _) => _rcmdDelegates
|
||||||
.Select(x => x.JoinToString('\n'))
|
.Where(entry => entry.HelpLines.Length > 0)
|
||||||
.JoinToString('\n') + '\n'
|
.SelectMany(entry => entry.HelpLines)
|
||||||
);
|
.JoinToString('\n') + '\n',
|
||||||
_rcmdDelegates.Add(["get info"], dbgr => dbgr.GetProcessInfo());
|
Array.Empty<string>()));
|
||||||
_rcmdDelegates.Add(["backtrace", "bt"], dbgr => dbgr.GetStackTrace());
|
|
||||||
_rcmdDelegates.Add(["registers", "reg"], dbgr => dbgr.GetRegisters());
|
_rcmdDelegates.Add(new RcmdEntry(["get info"], (dbgr, _) => dbgr.GetProcessInfo(), ["get info"]));
|
||||||
_rcmdDelegates.Add(["minidump"], dbgr => dbgr.GetMinidump());
|
_rcmdDelegates.Add(new RcmdEntry(["backtrace", "bt"], (dbgr, _) => dbgr.GetStackTrace(), ["backtrace", "bt"]));
|
||||||
|
_rcmdDelegates.Add(new RcmdEntry(["registers", "reg"], (dbgr, _) => dbgr.GetRegisters(), ["registers", "reg"]));
|
||||||
|
_rcmdDelegates.Add(new RcmdEntry(["minidump"], (dbgr, _) => dbgr.GetMinidump(), ["minidump"]));
|
||||||
|
_rcmdDelegates.Add(new RcmdEntry(["get mappings"], (dbgr, args) => dbgr.GetMemoryMappings(args), ["get mappings", "get mappings {address}"]));
|
||||||
|
_rcmdDelegates.Add(new RcmdEntry(["get mapping"], (dbgr, args) => dbgr.GetMemoryMapping(args), ["get mapping {address}"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<string[], Func<Debugger, string>> _rcmdDelegates = new();
|
private static readonly List<RcmdEntry> _rcmdDelegates = [];
|
||||||
|
|
||||||
public static Func<Debugger, string> FindRcmdDelegate(string command)
|
public static string CallRcmdDelegate(Debugger debugger, string command)
|
||||||
{
|
{
|
||||||
Func<Debugger, string> searchResult = _ => $"Unknown command: {command}\n";
|
string originalCommand = command ?? string.Empty;
|
||||||
|
string trimmedCommand = originalCommand.Trim();
|
||||||
|
|
||||||
foreach ((string[] names, Func<Debugger, string> dlg) in _rcmdDelegates)
|
foreach (RcmdEntry entry in _rcmdDelegates)
|
||||||
{
|
{
|
||||||
if (names.ContainsIgnoreCase(command.Trim()))
|
foreach (string name in entry.Names)
|
||||||
{
|
{
|
||||||
searchResult = dlg;
|
if (trimmedCommand.Equals(name, StringComparison.OrdinalIgnoreCase))
|
||||||
break;
|
{
|
||||||
|
return entry.Handler(debugger, string.Empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmedCommand.Length > name.Length &&
|
||||||
|
trimmedCommand.StartsWith(name, StringComparison.OrdinalIgnoreCase) &&
|
||||||
|
char.IsWhiteSpace(trimmedCommand[name.Length]))
|
||||||
|
{
|
||||||
|
string arguments = trimmedCommand[name.Length..].TrimStart();
|
||||||
|
return entry.Handler(debugger, arguments);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return searchResult;
|
return $"Unknown command: {originalCommand}\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetStackTrace()
|
public string GetStackTrace()
|
||||||
|
|
@ -86,5 +134,181 @@ namespace Ryujinx.HLE.Debugger
|
||||||
return $"Error getting process info: {e.Message}\n";
|
return $"Error getting process info: {e.Message}\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public string GetMemoryMappings(string arguments)
|
||||||
|
{
|
||||||
|
if (Process?.MemoryManager is not { } memoryManager)
|
||||||
|
{
|
||||||
|
return "No application process found\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
string trimmedArgs = arguments?.Trim() ?? string.Empty;
|
||||||
|
|
||||||
|
ulong startAddress = 0;
|
||||||
|
if (!string.IsNullOrEmpty(trimmedArgs))
|
||||||
|
{
|
||||||
|
if (!TryParseAddressArgument(trimmedArgs, out startAddress))
|
||||||
|
{
|
||||||
|
return $"Invalid address: {trimmedArgs}\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong requestedAddress = startAddress;
|
||||||
|
ulong currentAddress = Math.Max(requestedAddress, memoryManager.AddrSpaceStart);
|
||||||
|
StringBuilder sb = new();
|
||||||
|
sb.AppendLine($"Mappings (starting from 0x{requestedAddress:x10}):");
|
||||||
|
|
||||||
|
if (currentAddress >= memoryManager.AddrSpaceEnd)
|
||||||
|
{
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
while (currentAddress < memoryManager.AddrSpaceEnd)
|
||||||
|
{
|
||||||
|
KMemoryInfo info = memoryManager.QueryMemory(currentAddress);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (info.Size == 0 || info.Address >= memoryManager.AddrSpaceEnd)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.AppendLine(FormatMapping(info, indent: true));
|
||||||
|
|
||||||
|
if (info.Address > ulong.MaxValue - info.Size)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong nextAddress = info.Address + info.Size;
|
||||||
|
if (nextAddress <= currentAddress)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentAddress = nextAddress;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public string GetMemoryMapping(string arguments)
|
||||||
|
{
|
||||||
|
if (Process?.MemoryManager is not { } memoryManager)
|
||||||
|
{
|
||||||
|
return "No application process found\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
string trimmedArgs = arguments?.Trim() ?? string.Empty;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(trimmedArgs))
|
||||||
|
{
|
||||||
|
return "Missing address argument for `get mapping`\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryParseAddressArgument(trimmedArgs, out ulong address))
|
||||||
|
{
|
||||||
|
return $"Invalid address: {trimmedArgs}\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
KMemoryInfo info = memoryManager.QueryMemory(address);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return FormatMapping(info, indent: false) + '\n';
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
KMemoryInfo.Pool.Release(info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string FormatMapping(KMemoryInfo info, bool indent)
|
||||||
|
{
|
||||||
|
ulong endAddress;
|
||||||
|
|
||||||
|
if (info.Size == 0)
|
||||||
|
{
|
||||||
|
endAddress = info.Address;
|
||||||
|
}
|
||||||
|
else if (info.Address > ulong.MaxValue - (info.Size - 1))
|
||||||
|
{
|
||||||
|
endAddress = ulong.MaxValue;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
endAddress = info.Address + info.Size - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
string prefix = indent ? " " : string.Empty;
|
||||||
|
return $"{prefix}0x{info.Address:x10} - 0x{endAddress:x10} {GetPermissionString(info)} {GetMemoryStateName(info.State)} {GetAttributeFlags(info)} [{info.IpcRefCount}, {info.DeviceRefCount}]";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetPermissionString(KMemoryInfo info)
|
||||||
|
{
|
||||||
|
if ((info.State & MemoryState.UserMask) == MemoryState.Unmapped)
|
||||||
|
{
|
||||||
|
return " ";
|
||||||
|
}
|
||||||
|
|
||||||
|
return info.Permission switch
|
||||||
|
{
|
||||||
|
KMemoryPermission.ReadAndExecute => "r-x",
|
||||||
|
KMemoryPermission.Read => "r--",
|
||||||
|
KMemoryPermission.ReadAndWrite => "rw-",
|
||||||
|
_ => "---"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetMemoryStateName(MemoryState state)
|
||||||
|
{
|
||||||
|
int stateIndex = (int)(state & MemoryState.UserMask);
|
||||||
|
if ((uint)stateIndex < _memoryStateNames.Length)
|
||||||
|
{
|
||||||
|
return _memoryStateNames[stateIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Unknown ";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool TryParseAddressArgument(string text, out ulong value)
|
||||||
|
{
|
||||||
|
value = 0;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(text))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
string trimmed = text.Trim();
|
||||||
|
|
||||||
|
if (trimmed.StartsWith("0x", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
trimmed = trimmed[2..];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (trimmed.Length == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ulong.TryParse(trimmed, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string GetAttributeFlags(KMemoryInfo info)
|
||||||
|
{
|
||||||
|
char locked = info.Attribute.HasFlag(MemoryAttribute.Borrowed) ? 'L' : '-';
|
||||||
|
char ipc = info.Attribute.HasFlag(MemoryAttribute.IpcMapped) ? 'I' : '-';
|
||||||
|
char device = info.Attribute.HasFlag(MemoryAttribute.DeviceMapped) ? 'D' : '-';
|
||||||
|
char uncached = info.Attribute.HasFlag(MemoryAttribute.Uncached) ? 'U' : '-';
|
||||||
|
|
||||||
|
return $"{locked}{ipc}{device}{uncached}";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -404,9 +404,8 @@ 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}");
|
||||||
|
|
||||||
Func<Debugger, string> rcmd = Debugger.FindRcmdDelegate(command);
|
string response = Debugger.CallRcmdDelegate(Debugger, command);
|
||||||
|
Processor.ReplyHex(response);
|
||||||
Processor.ReplyHex(rcmd(Debugger));
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue