Tools for debugging patch transpilers.

This commit is contained in:
Westin Miller
2017-10-31 23:42:25 -07:00
parent 0558675132
commit 98aae10126
3 changed files with 148 additions and 16 deletions

View File

@@ -29,20 +29,30 @@ namespace Torch.Managers.PatchManager
internal void Commit()
{
if (!Prefixes.HasChanges() && !Suffixes.HasChanges() && !Transpilers.HasChanges())
return;
Revert();
try
{
if (!Prefixes.HasChanges() && !Suffixes.HasChanges() && !Transpilers.HasChanges())
return;
Revert();
if (Prefixes.Count == 0 && Suffixes.Count == 0 && Transpilers.Count == 0)
return;
_log.Debug($"Begin patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
var patch = ComposePatchedMethod();
if (Prefixes.Count == 0 && Suffixes.Count == 0 && Transpilers.Count == 0)
return;
_log.Debug(
$"Begin patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
var patch = ComposePatchedMethod();
_revertAddress = AssemblyMemory.GetMethodBodyStart(_method);
var newAddress = AssemblyMemory.GetMethodBodyStart(patch);
_revertData = AssemblyMemory.WriteJump(_revertAddress, newAddress);
_pinnedPatch = GCHandle.Alloc(patch);
_log.Debug($"Done patching {_method.DeclaringType?.FullName}#{_method.Name}({string.Join(", ", _method.GetParameters().Select(x => x.ParameterType.Name))})");
_revertAddress = AssemblyMemory.GetMethodBodyStart(_method);
var newAddress = AssemblyMemory.GetMethodBodyStart(patch);
_revertData = AssemblyMemory.WriteJump(_revertAddress, newAddress);
_pinnedPatch = GCHandle.Alloc(patch);
_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()
@@ -156,7 +166,7 @@ namespace Torch.Managers.PatchManager
target.EmitComment("Prefixes End");
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.MarkLabel(labelAfterOriginalContent);

View File

@@ -158,6 +158,24 @@ namespace Torch.Managers.PatchManager
/// </summary>
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>
@@ -167,6 +185,7 @@ namespace Torch.Managers.PatchManager
Prefixes = new MethodRewriteSet(parentPattern?.Prefixes);
Transpilers = new MethodRewriteSet(parentPattern?.Transpilers);
Suffixes = new MethodRewriteSet(parentPattern?.Suffixes);
_parent = parentPattern;
}
}
}

View File

@@ -1,7 +1,9 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Text;
using NLog;
using Torch.Managers.PatchManager.MSIL;
@@ -11,7 +13,9 @@ namespace Torch.Managers.PatchManager.Transpile
{
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);
context.Read();
@@ -40,8 +44,107 @@ namespace Torch.Managers.PatchManager.Transpile
methodContent = (IEnumerable<MsilInstruction>)transpiler.Invoke(null, paramList.ToArray());
}
methodContent = FixBranchAndReturn(methodContent, retLabel);
foreach (var k in methodContent)
k.Emit(output);
if (logMsil)
{
var list = methodContent.ToList();
IntegrityAnalysis(list);
foreach (var k in list)
k.Emit(output);
}
else
{
foreach (var k in methodContent)
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)