Skip to content

Instantly share code, notes, and snippets.

@pawlos
Created February 27, 2020 18:07
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save pawlos/0ac1a64e00593cb1ec40263775b6df65 to your computer and use it in GitHub Desktop.
Save pawlos/0ac1a64e00593cb1ec40263775b6df65 to your computer and use it in GitHub Desktop.
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using Mono.Cecil;
using Mono.Cecil.Cil;
namespace SimpleILMeasurement
{
class Program
{
static void Main(string[] args)
{
if (args.Length != 1)
{
Console.WriteLine("SimpleILMeasurement.exe <assembly>");
return;
}
string fileName = args[0];
ModuleDefinition module = ModuleDefinition.ReadModule(fileName);
if (module.Kind == ModuleKind.Windows)
module.Kind = ModuleKind.Console;
var stopWatchCtor =
module.ImportReference(typeof(Stopwatch).GetConstructor(BindingFlags.Instance | BindingFlags.Public,
null, new Type[0], null));
var stopWatchRef = module.ImportReference(typeof(Stopwatch));
var stopWatchStartRef = module.ImportReference(typeof(Stopwatch).GetMethod("Start", new Type[0]));
var stopWatchStopRef = module.ImportReference(typeof(Stopwatch).GetMethod("Stop", new Type[0]));
var stopWatchElapsedRef = module.ImportReference(typeof(Stopwatch).GetProperty("Elapsed")?.GetMethod);
var consoleWriteLineRef =
module.ImportReference(typeof(Console).GetMethod("WriteLine", new[] {typeof(string), typeof(object)}));
var timeSpanRef = module.ImportReference(typeof(TimeSpan));
foreach (var type in module.Types)
{
foreach (var method in type.Methods)
{
if (!method.HasBody) continue;
var ilProcessor = method.Body.GetILProcessor();
var variableDeclaration = new VariableDefinition(stopWatchRef);
method.Body.Variables.Add(variableDeclaration);
var firstInstruction = method.Body.Instructions.First();
// var v0 = new Stopwatch();
// v0.Start();
var newObjOpcode = ilProcessor.Create(OpCodes.Newobj, stopWatchCtor);
var stLocOpcode = ilProcessor.Create(OpCodes.Stloc, variableDeclaration);
var ldLocOpcode = ilProcessor.Create(OpCodes.Ldloc, variableDeclaration);
var callStartOpcode = ilProcessor.Create(OpCodes.Call, stopWatchStartRef);
ilProcessor.InsertBefore(firstInstruction, newObjOpcode);
ilProcessor.InsertBefore(firstInstruction, stLocOpcode);
ilProcessor.InsertBefore(firstInstruction, ldLocOpcode);
ilProcessor.InsertBefore(firstInstruction, callStartOpcode);
// original code
// v0.Stop();
// Console.WriteLine($"---Method {method.Name} took {0}", v0.Elapsed);
var ldLocOpcode2 = ilProcessor.Create(OpCodes.Ldloc, variableDeclaration);
var callStopOpcode = ilProcessor.Create(OpCodes.Call, stopWatchStopRef);
var ldStrOpcode = ilProcessor.Create(OpCodes.Ldstr, $"---Method {method.Name} took {{0}}");
var callElapsedOpcode = ilProcessor.Create(OpCodes.Call, stopWatchElapsedRef);
var callWriteLineOpcode = ilProcessor.Create(OpCodes.Call, consoleWriteLineRef);
var boxOpcode = ilProcessor.Create(OpCodes.Box, timeSpanRef);
var lastInstruction = ilProcessor.Body.Instructions.Last();
ilProcessor.Replace(lastInstruction, ldLocOpcode2);
ilProcessor.Body.Instructions.Add(callStopOpcode);
ilProcessor.Body.Instructions.Add(ldStrOpcode);
ilProcessor.Body.Instructions.Add(ldLocOpcode);
ilProcessor.Body.Instructions.Add(callElapsedOpcode);
ilProcessor.Body.Instructions.Add(boxOpcode);
ilProcessor.Body.Instructions.Add(callWriteLineOpcode);
ilProcessor.Body.Instructions.Add(lastInstruction);
foreach (var bodyInstruction in ilProcessor.Body.Instructions)
{
if (bodyInstruction.OpCode != OpCodes.Br && bodyInstruction.OpCode != OpCodes.Br_S) continue;
if (((Instruction)bodyInstruction.Operand).OpCode != OpCodes.Ret) continue;
bodyInstruction.Operand = ldLocOpcode2;
}
}
}
module.Write(Path.GetFileNameWithoutExtension(fileName) + ".modified" + Path.GetExtension(fileName));
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment