mirror of
https://git.naxdy.org/Mirror/Ryujinx.git
synced 2025-12-16 12:09:51 +00:00
gdb: add monitor get mapping
monitor get mappings monitor get mappings <address> monitor get mapping <address>
This commit is contained in:
parent
3a593b6084
commit
ccca2e8282
@ -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,167 @@ 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong startAddress = 0;
|
||||||
|
string trimmedArgs = arguments?.Trim() ?? string.Empty;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(trimmedArgs))
|
||||||
|
{
|
||||||
|
string value = trimmedArgs.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? trimmedArgs[2..]
|
||||||
|
: trimmedArgs;
|
||||||
|
|
||||||
|
if (value.Length == 0 || !ulong.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, 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";
|
||||||
|
}
|
||||||
|
|
||||||
|
ulong address;
|
||||||
|
|
||||||
|
string value = trimmedArgs.StartsWith("0x", StringComparison.OrdinalIgnoreCase)
|
||||||
|
? trimmedArgs[2..]
|
||||||
|
: trimmedArgs;
|
||||||
|
|
||||||
|
if (value.Length == 0 || !ulong.TryParse(value, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out 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 string GetAttributeFlags(KMemoryInfo info)
|
||||||
|
{
|
||||||
|
char locked = info.Attribute.HasFlag(MemoryAttribute.PermissionLocked) ? '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…
Reference in New Issue
Block a user