Tools for debugging patch transpilers.
This commit is contained in:
@@ -28,6 +28,8 @@ namespace Torch.Managers.PatchManager
|
|||||||
private GCHandle? _pinnedPatch;
|
private GCHandle? _pinnedPatch;
|
||||||
|
|
||||||
internal void Commit()
|
internal void Commit()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (!Prefixes.HasChanges() && !Suffixes.HasChanges() && !Transpilers.HasChanges())
|
if (!Prefixes.HasChanges() && !Suffixes.HasChanges() && !Transpilers.HasChanges())
|
||||||
return;
|
return;
|
||||||
@@ -35,14 +37,22 @@ namespace Torch.Managers.PatchManager
|
|||||||
|
|
||||||
if (Prefixes.Count == 0 && Suffixes.Count == 0 && Transpilers.Count == 0)
|
if (Prefixes.Count == 0 && Suffixes.Count == 0 && Transpilers.Count == 0)
|
||||||
return;
|
return;
|
||||||
_log.Debug($"Begin patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
|
_log.Debug(
|
||||||
|
$"Begin patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
|
||||||
var patch = ComposePatchedMethod();
|
var patch = ComposePatchedMethod();
|
||||||
|
|
||||||
_revertAddress = AssemblyMemory.GetMethodBodyStart(_method);
|
_revertAddress = AssemblyMemory.GetMethodBodyStart(_method);
|
||||||
var newAddress = AssemblyMemory.GetMethodBodyStart(patch);
|
var newAddress = AssemblyMemory.GetMethodBodyStart(patch);
|
||||||
_revertData = AssemblyMemory.WriteJump(_revertAddress, newAddress);
|
_revertData = AssemblyMemory.WriteJump(_revertAddress, newAddress);
|
||||||
_pinnedPatch = GCHandle.Alloc(patch);
|
_pinnedPatch = GCHandle.Alloc(patch);
|
||||||
_log.Debug($"Done patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
|
_log.Debug(
|
||||||
|
$"Done patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
_log.Fatal($"Error patching {_method.DeclaringType?.FullName}#{_method}");
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Revert()
|
internal void Revert()
|
||||||
@@ -156,7 +166,7 @@ namespace Torch.Managers.PatchManager
|
|||||||
target.EmitComment("Prefixes End");
|
target.EmitComment("Prefixes End");
|
||||||
|
|
||||||
target.EmitComment("Original Begin");
|
target.EmitComment("Original Begin");
|
||||||
MethodTranspiler.Transpile(_method, (type) => new MsilLocal(target.DeclareLocal(type)), Transpilers, target, labelAfterOriginalContent);
|
MethodTranspiler.Transpile(_method, (type) => new MsilLocal(target.DeclareLocal(type)), Transpilers, target, labelAfterOriginalContent, PrintMsil);
|
||||||
target.EmitComment("Original End");
|
target.EmitComment("Original End");
|
||||||
|
|
||||||
target.MarkLabel(labelAfterOriginalContent);
|
target.MarkLabel(labelAfterOriginalContent);
|
||||||
|
@@ -158,6 +158,24 @@ namespace Torch.Managers.PatchManager
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public MethodRewriteSet Suffixes { get; }
|
public MethodRewriteSet Suffixes { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should the resulting MSIL of this patch operation be printed.
|
||||||
|
/// </summary>
|
||||||
|
public bool PrintMsil
|
||||||
|
{
|
||||||
|
get => _parent?.PrintMsil ?? _printMsilBacking;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (_parent != null)
|
||||||
|
_parent.PrintMsil = value;
|
||||||
|
else
|
||||||
|
_printMsilBacking = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool _printMsilBacking;
|
||||||
|
private readonly MethodRewritePattern _parent;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
///
|
///
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -167,6 +185,7 @@ namespace Torch.Managers.PatchManager
|
|||||||
Prefixes = new MethodRewriteSet(parentPattern?.Prefixes);
|
Prefixes = new MethodRewriteSet(parentPattern?.Prefixes);
|
||||||
Transpilers = new MethodRewriteSet(parentPattern?.Transpilers);
|
Transpilers = new MethodRewriteSet(parentPattern?.Transpilers);
|
||||||
Suffixes = new MethodRewriteSet(parentPattern?.Suffixes);
|
Suffixes = new MethodRewriteSet(parentPattern?.Suffixes);
|
||||||
|
_parent = parentPattern;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using System.Reflection.Emit;
|
using System.Reflection.Emit;
|
||||||
|
using System.Text;
|
||||||
using NLog;
|
using NLog;
|
||||||
using Torch.Managers.PatchManager.MSIL;
|
using Torch.Managers.PatchManager.MSIL;
|
||||||
|
|
||||||
@@ -11,7 +13,9 @@ namespace Torch.Managers.PatchManager.Transpile
|
|||||||
{
|
{
|
||||||
public static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
public static readonly Logger _log = LogManager.GetCurrentClassLogger();
|
||||||
|
|
||||||
internal static void Transpile(MethodBase baseMethod, Func<Type, MsilLocal> localCreator, IEnumerable<MethodInfo> transpilers, LoggingIlGenerator output, Label? retLabel)
|
internal static void Transpile(MethodBase baseMethod, Func<Type, MsilLocal> localCreator,
|
||||||
|
IEnumerable<MethodInfo> transpilers, LoggingIlGenerator output, Label? retLabel,
|
||||||
|
bool logMsil)
|
||||||
{
|
{
|
||||||
var context = new MethodContext(baseMethod);
|
var context = new MethodContext(baseMethod);
|
||||||
context.Read();
|
context.Read();
|
||||||
@@ -40,9 +44,108 @@ namespace Torch.Managers.PatchManager.Transpile
|
|||||||
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, paramList.ToArray());
|
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, paramList.ToArray());
|
||||||
}
|
}
|
||||||
methodContent = FixBranchAndReturn(methodContent, retLabel);
|
methodContent = FixBranchAndReturn(methodContent, retLabel);
|
||||||
|
if (logMsil)
|
||||||
|
{
|
||||||
|
var list = methodContent.ToList();
|
||||||
|
IntegrityAnalysis(list);
|
||||||
|
foreach (var k in list)
|
||||||
|
k.Emit(output);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
foreach (var k in methodContent)
|
foreach (var k in methodContent)
|
||||||
k.Emit(output);
|
k.Emit(output);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Analyzes the integrity of a set of instructions.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instructions">instructions</param>
|
||||||
|
private static void IntegrityAnalysis(List<MsilInstruction> instructions)
|
||||||
|
{
|
||||||
|
var targets = new Dictionary<MsilLabel, int>();
|
||||||
|
for (var i = 0; i < instructions.Count; i++)
|
||||||
|
foreach (var label in instructions[i].Labels)
|
||||||
|
{
|
||||||
|
if (targets.TryGetValue(label, out var other))
|
||||||
|
_log.Warn($"Label {label} is applied to ({i}: {instructions[i]}) and ({other}: {instructions[other]})");
|
||||||
|
targets[label] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
var reparsed = new HashSet<MsilLabel>();
|
||||||
|
var labelStackSize = new Dictionary<MsilLabel, Dictionary<int, int>>();
|
||||||
|
var stack = 0;
|
||||||
|
var unreachable = false;
|
||||||
|
var data = new StringBuilder[instructions.Count];
|
||||||
|
for (var i = 0; i < instructions.Count; i++)
|
||||||
|
{
|
||||||
|
var k = instructions[i];
|
||||||
|
var line = (data[i] ?? (data[i] = new StringBuilder())).Clear();
|
||||||
|
if (!unreachable)
|
||||||
|
{
|
||||||
|
foreach (var label in k.Labels)
|
||||||
|
{
|
||||||
|
if (!labelStackSize.TryGetValue(label, out Dictionary<int, int> otherStack))
|
||||||
|
labelStackSize[label] = otherStack = new Dictionary<int, int>();
|
||||||
|
|
||||||
|
otherStack.Add(i - 1, stack);
|
||||||
|
if (otherStack.Values.Distinct().Count() > 1 || (otherStack.Count == 1 && !otherStack.ContainsValue(stack)))
|
||||||
|
{
|
||||||
|
string otherDesc = string.Join(", ", otherStack.Select(x => $"{x.Key:X4}=>{x.Value}"));
|
||||||
|
line.AppendLine($"WARN// | Label {label} has multiple entry stack sizes ({otherDesc})");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
foreach (var label in k.Labels)
|
||||||
|
{
|
||||||
|
if (!labelStackSize.TryGetValue(label, out var entry))
|
||||||
|
continue;
|
||||||
|
string desc = string.Join(", ", entry.Select(x => $"{x.Key:X4}=>{x.Value}"));
|
||||||
|
line.AppendLine($"// \\/ Label {label} has stack sizes {desc}");
|
||||||
|
if (unreachable && entry.Any())
|
||||||
|
{
|
||||||
|
stack = entry.Values.First();
|
||||||
|
unreachable = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
stack += k.StackChange();
|
||||||
|
line.AppendLine($"{i:X4} S:{stack:D2} dS:{k.StackChange():+0;-#}\t{k}" + (unreachable ? "\t// UNREACHABLE" : ""));
|
||||||
|
if (k.Operand is MsilOperandBrTarget br)
|
||||||
|
{
|
||||||
|
if (!targets.ContainsKey(br.Target))
|
||||||
|
line.AppendLine($"WARN// ^ Unknown target {br.Target}");
|
||||||
|
|
||||||
|
if (!labelStackSize.TryGetValue(br.Target, out Dictionary<int, int> otherStack))
|
||||||
|
labelStackSize[br.Target] = otherStack = new Dictionary<int, int>();
|
||||||
|
|
||||||
|
otherStack[i] = stack;
|
||||||
|
if (otherStack.Values.Distinct().Count() > 1 || (otherStack.Count == 1 && !otherStack.ContainsValue(stack)))
|
||||||
|
{
|
||||||
|
string otherDesc = string.Join(", ", otherStack.Select(x => $"{x.Key:X4}=>{x.Value}"));
|
||||||
|
line.AppendLine($"WARN// ^ Label {br.Target} has multiple entry stack sizes ({otherDesc})");
|
||||||
|
}
|
||||||
|
if (targets.TryGetValue(br.Target, out var target) && target < i && reparsed.Add(br.Target))
|
||||||
|
{
|
||||||
|
i = target - 1;
|
||||||
|
unreachable = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (k.OpCode == OpCodes.Br || k.OpCode == OpCodes.Br_S)
|
||||||
|
unreachable = true;
|
||||||
|
}
|
||||||
|
foreach (var k in data)
|
||||||
|
foreach (var line in k.ToString().Split('\n'))
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(line))
|
||||||
|
continue;
|
||||||
|
if (line.StartsWith("WARN", StringComparison.OrdinalIgnoreCase))
|
||||||
|
_log.Warn(line.Substring(4).Trim());
|
||||||
|
else
|
||||||
|
_log.Info(line.Trim());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal static void Emit(IEnumerable<MsilInstruction> input, LoggingIlGenerator output)
|
internal static void Emit(IEnumerable<MsilInstruction> input, LoggingIlGenerator output)
|
||||||
{
|
{
|
||||||
|
Reference in New Issue
Block a user