internal class AssemblyTracing
{
/// <nn />
private readonly string _assemblyPath;
/// <summary>
/// Constructor taking initializing parameter.
/// </summary>
/// <param name="assemblyPath">The path of the assembly where to inject tracing code.</param>
public AssemblyTracing(string assemblyPath)
{
_assemblyPath = assemblyPath;
}
/// <summary>
/// Injects tracing code to the assembly that this class instance has been created for.
/// </summary>
public void InjectTracingToAssembly()
{
var assemblyDefinition = AssemblyDefinition.ReadAssembly(_assemblyPath);
Console.WriteLine("Injecting trace code to assembly \"{0}\"", _assemblyPath);
foreach (var moduleDefinition in assemblyDefinition.Modules)
{
foreach (var typeDefinition in moduleDefinition.Types)
{
InjectTracingToTypeOfModule(typeDefinition, moduleDefinition);
}
}
assemblyDefinition.Write(_assemblyPath, new WriterParameters { WriteSymbols = true });
}
/// <nn />
private void InjectTracingToTypeOfModule(TypeDefinition type, ModuleDefinition moduleDefinition)
{
if (type.IsInterface || type.IsEnum || !type.IsClass)
{
return;
}
foreach (var typeDefinition in type.NestedTypes)
{
InjectTracingToTypeOfModule(typeDefinition, moduleDefinition);
}
FieldDefinition fTraceDomainField = AddFTraceDomainFieldToType(type, moduleDefinition);
MethodDefinition staticConstructor = GetStaticConstructor(type);
if (staticConstructor == null)
{
staticConstructor = AddStaticConstructorForFTraceDomain(type, moduleDefinition);
}
AddInitialisationOfFTraceDomainFieldToStaticConstructor(
moduleDefinition,
type,
staticConstructor,
fTraceDomainField);
foreach (var methodDefinition in type.Methods)
{
if (methodDefinition.IsConstructor /*&& methodDefinition.IsStatic*/)
{
continue;
}
if (methodDefinition.HasBody)
{
EncapsulateMethodBodyWithTryFinallyBlock(moduleDefinition, methodDefinition, fTraceDomainField);
}
}
}
/// <nn />
private static MethodDefinition GetStaticConstructor(TypeDefinition type)
{
foreach (var method in type.Methods)
{
if (method.IsConstructor && method.IsStatic)
{
return method;
}
}
return null;
}
/// <nn />
private static Instruction FindFirstInstructionSkipCtor(MethodDefinition med)
{
MethodBody body = med.Body;
if (med.IsConstructor && !med.IsStatic)
{
return body.Instructions.Skip(2).First();
}
return body.Instructions.First();
}
/// <nn />
private static Instruction FixReturns(MethodDefinition med, ModuleDefinition mod)
{
MethodBody body = med.Body;
Instruction formallyLastInstruction = body.Instructions.Last();
Instruction lastLeaveInstruction = null;
if (med.ReturnType == mod.TypeSystem.Void)
{
var instructions = body.Instructions;
var lastRet = Instruction.Create(OpCodes.Ret);
instructions.Add(lastRet);
for (var index = 0; index < instructions.Count - 1; index++)
{
var instruction = instructions[index];
if (instruction.OpCode == OpCodes.Ret)
{
Instruction leaveInstruction = Instruction.Create(OpCodes.Leave, lastRet);
if (instruction == formallyLastInstruction)
{
lastLeaveInstruction = leaveInstruction;
}
instructions[index] = leaveInstruction;
}
}
FixBranchTargets(lastLeaveInstruction, formallyLastInstruction, body);
return lastRet;
}
else
{
var instructions = body.Instructions;
var returnVariable = new VariableDefinition("methodTimerReturn", med.ReturnType);
body.Variables.Add(returnVariable);
var lastLd = Instruction.Create(OpCodes.Ldloc, returnVariable);
instructions.Add(lastLd);
instructions.Add(Instruction.Create(OpCodes.Ret));
for (var index = 0; index < instructions.Count - 2; index++)
{
var instruction = instructions[index];
if (instruction.OpCode == OpCodes.Ret)
{
Instruction leaveInstruction = Instruction.Create(OpCodes.Leave, lastLd);
if (instruction == formallyLastInstruction)
{
lastLeaveInstruction = leaveInstruction;
}
instructions[index] = leaveInstruction;
instructions.Insert(index, Instruction.Create(OpCodes.Stloc, returnVariable));
index++;
}
}
FixBranchTargets(lastLeaveInstruction, formallyLastInstruction, body);
return lastLd;
}
}
/// <nn />
private static void FixBranchTargets(
Instruction lastLeaveInstruction,
Instruction formallyLastRetInstruction,
MethodBody body)
{
for (var index = 0; index < body.Instructions.Count - 2; index++)
{
var instruction = body.Instructions[index];
if (instruction.Operand != null && instruction.Operand == formallyLastRetInstruction)
{
instruction.Operand = lastLeaveInstruction;
}
}
}
/// <nn />
private static void EncapsulateMethodBodyWithTryFinallyBlock(
ModuleDefinition mod,
MethodDefinition med,
FieldDefinition fTraceDomainField)
{
if (med.Body == null || med.Body.Instructions.Count <= 0)
{
Console.WriteLine(med.Name + " has not body or no instructions in body");
return;