Everything about my daily life as a programmer/Electrical Engineer!

Mono.Cecil Profiler

So we have been trying to develop a profiler for a long time.  All the posts seem to require an inordinate amount ofMSIL knowledge or to know C++ to interface with the CLR directly.  Both of these approaches were painful.  All I wanted to do was inject a Profile.Start and Profile.End into our code base for every class giving us a simple profiler.  With Mono.Cecil I could do exactly that, and without Mono installed on my computer.  Go get the original example from the Mono.Cecil website or just download it directly here. There is a dll called lib.  I just added that to my solution in VS2008 and wrote the following simple profiler.

 

 

static void Main(string[] args)
{
string pathBin = @"C:\Users\michael\Documents\Visual Studio 2008\Projects\ConsoleApplication1\ConsoleApplication2\bin\Debug\ConsoleApplication2.exe";

MethodInfo startMethod = typeof(ProfileClient).GetMethod("Start", new Type[] { typeof(string) });
MethodInfo endMethod =typeof(ProfileClient).GetMethod("End", new Type[] { typeof(string) });

AssemblyDefinition assembly = AssemblyFactory.GetAssembly(pathBin);

foreach (TypeDefinition type in assembly.MainModule.Types)
{
if (type.Name != "<Module>")
{
foreach (MethodDefinition method in type.Methods)
{
CilWorker worker = method.Body.CilWorker;
string sentence = method.Name;
MethodReference start=assembly.MainModule.Import(startMethod);
MethodReference end= assembly.MainModule.Import(endMethod);

//Creates the MSIL instruction for inserting the sentence
Instruction startSentence = worker.Create(OpCodes.Ldstr, sentence);
Instruction endSentence= worker.Create(OpCodes.Ldstr, sentence);

List<Instruction> parameters = new List<Instruction>();

//Creates the CIL instruction for calling the start and end
Instruction callStart= worker.Create(OpCodes.Call, start);
Instruction callEnd= worker.Create(OpCodes.Call, end);

//-2 cause you need to go before the return opcode
Instruction lastinstruction= method.Body.Instructions[method.Body.Instructions.Count-2];
worker.InsertAfter(lastinstruction, endSentence);
worker.InsertAfter(endSentence, callEnd);

Instruction firstinstruction = method.Body.Instructions[0];
worker.InsertBefore(firstinstruction, startSentence);
worker.InsertAfter(startSentence, callStart);


}
}
}

AssemblyFactory.SaveAssembly(assembly, pathBin.Replace(".exe","")+"-new-.exe");
}


 



public class ProfileClient
{
public static void Start(string method,params object[] args)
{
Console.WriteLine("Start " + method+">");
}

public static void End(string method, params object[] args)
{
Console.WriteLine(">End "+ method);
}
}



 



class Program
{
static void Main(string[] args)
{
Console.WriteLine("My Code2 ");
}
}




So if you build the last program and run it, it will output



My Code2



Once you run the second console application and then run ConsoleApplication2-new-.exe you will get



Start Main>

My Code2 ?


>End Main



I’m still not sure where the ? comes from but for now this simple profiler works and will be the basis of our more complex profiler we are planning to start constructing.

1 comment:

Jasper said...

This is what I was looking for.. :)
I somehow missed the link for the sample cecil code. Thanks for posting it..

Cheers
-Jasper