Create Your Own CLR Profiler in C#
Before we go any further, I’d like to clarify something: It is not feasible to create a high-performance live CLR profiler in C#, because of the way the core profiling API and the CLR works. It’s unmanaged C++ all the way down. If you are interested in knowing more about how to build one, here are two excellent sources:
However, it is feasible to build a CLR profiler in C# that can be used during development and testing for things such as memory and code analysis on servers where Visual Studio is not available.
That’s all been made possible because of the excellent work done by the Microsoft Research team that built PEX (and Moles) a couple of years ago. In order for PEX to work its magic — and if you don’t know what that is, you should follow the link above and also check out the Code Digger VS Extension — the team needed to be able to instrument every single bit of code produced by the C# compiler. And to do that, they implemented the C++ profiling interfaces and wrapped it all up in the publicly available Microsoft.ExtendedReflection.dll.
The Microsoft.ExtendedReflection.dll was primarily for PEX to use, but a little bit of documentation was released along with a few samples. In order to get to that, you will have to download and install the original PEX and then go to this folder:
C:\Program Files\Microsoft Pex\Documentation\pex.samples\samples\ExtendedReflection
The “Tracing” sample contains a ready-made implementation of a CLR profiler in C# that you can extend to fit your purposes. Here are a few code snippets from the initialization:
var application = args[0]; // For instance, ConsoleApplication1.exe ... var startInfo = new ProcessStartInfo(application, null); startInfo.UseShellExecute = false; var env = new StringDictionary(); SetMonitoringEnvironment(userAssembly, userType, env); foreach(DictionaryEntry de in env) { startInfo.EnvironmentVariables[(string)de.Key] = (string)de.Value; } using (var process = Process.Start(startInfo)) { process.WaitForExit(); exitCode = process.ExitCode; } ... ControllerSetUp.SetMonitoringEnvironmentVariables( environmentVariables, MonitorInstrumentationFlags.EnterLeaveMethod | MonitorInstrumentationFlags.UnwindMethod,// | //MonitorInstrumentationFlags.MethodArguments | //MonitorInstrumentationFlags.MethodReturnArguments, false, userAssembly.Location, userType.FullName, new string[] { Metadata<Program>.Assembly.Location }, // substitutions assemblies new string[] { typeof(System.Diagnostics.Debug).FullName }, // types to monitor null, // types to exclude to monitor new string[] { "ConsoleApplication1" }, // namespaces to monitor null, // namespace to exclude to monitor new string[] { "ConsoleApplication1" }, // assemblies to monitor new string[] { Metadata<Object>.Assembly.ShortName, Metadata<_ThreadContext>.Assembly.ShortName, userAssembly.ShortName }, null, // types to project null, null, null, null, null, @"c:\pex\bin\debuginstr.txt", // log file name false, // crash on failure null, // target clr version true, // protect all .cctors false, // disable mscorlib supressions ProfilerInteraction.Fail, // allow loading external profiler null // additional instrument attribute full name );
From there, you can implement any or all of the callbacks defined in the documentation. For instance:
public override EnterMethodFlags EnterMethod(Method method) { // Do whatever you want here. sw.WriteLine("level {1} <enter> {0}", method.FullName, level); this.indent(+1); return EnterMethodFlags.NeedArguments; }
That’s pretty clever. So now you can for instance start counting which methods get called the most across any namespace you want to monitor — which by the way is one of the typical performance killers for developers who are fond of Linq without actually understanding how it works under the covers.
Have fun profiling!
Can i use this dll “Microsoft.ExtendedReflection.dll” to build a commercial application ?
I actually don’t know, but I assume it would be possible. The closest I have seen is this comment by one of the authors: http://social.msdn.microsoft.com/Forums/en-US/1aff28e3-16e2-4667-b697-621700a780c3/extendedreflection-license?forum=pex
Is it possible to get code coverage(number of lines executed in a method/total no of lines) for each of the methods executed?
Hi again!
It depends on what you want. The profiler injects hooks at various points (before enter method, after exit method, etc) — the “target” code is still compiled and run as normal. So you would be able to count any calls to any methods that you profile but that’s about it. I’m not sure that covers your use-case. Best way to find out really is to try it yourself!