How to Create Working CLR Profiler

One of my recent project requires me to delve into the inner workings of the Microsoft’s .NET CLR Infrastructure. Specifically how the CLR performs its task to load, execute and unloads a piece of .NET Application.

After performing some research, I came across an interesting article that will provide me a head start, actually it is rather old one, from MSDN 2001 Article titled “Under the Hood – The .NET Profiling API and the DNProfiler Tool” written by Matt Pietrek.

What makes this article very valuable is that it also provides the sample application to implement the CLR profiling mechanism, so I do not have to write it from scratch.

And I tried to download this application in no time, but to my surprise, what I found is a series of dead-links.

Eventually, I found the similar one, which happens to resides in the part of utilities from SSCLI ver 1.0 from Microsoft’s website. It is located in samples\utilities\dnprofiler folder after the SSCLI is expanded.

The fact that what I got is not the same original source code created by Matt Pietrek is stated in the official dnprofiler documentation inside the SSCLI as follows :

“This sample differs from the code described by the MSDN article in the use of COM and the registry. COM registration is replaced by an environment variable, COR_PROFILER_DLL in profiler_on.bat, that contains the path to the DNProfiler.dll on Microsoft Windows®.”

Because it seems that I can’t obtain the pristine source code, I eventually resort to use this derived source code. But the issue is that in order I can use this sample code, I have to construct the entire SSCLI Framework from scratch.

This is like kind of shooting the mosquito using the canon, and besides, I do not want to use the said “experimental” framework, I just want to applied it into my existing .NET Framework v2.0 infrastructure in my machine.

After considering some options to be carried out, I decided to ‘resurrect’ again the ‘dead’ CLR profiling application by re-creating the independent application based on the existing DNPROFILER source code from SSCLI v1.0. I am doing this using VS 2005, and after resolving some compiler and functional issues, the application, called MyProfile.DLL is successfully placed in my debug directory.

I am now ready to test it. After executing the profiling_on batch file as instructed from the article, I activated the small .net console application, hoping to find the DNProfiler.out in application’s folder.

I was disappointed to find that the file is not there. So, this will bring me to another issue, i.e. how to perform the debugging of this kind of construct, just to pinpoint where’s the location of this application generates error, or whether this DLL is really got called from the CLR.

I have no executable to work with, just the DLL file, and this DLL depends on certain environment variable to be called by existing .NET Framework called Cor_Enable_Profiling, COR_PROFILER, and COR_PROFILER_DLL.

Because my small .net application is using the command prompt window, at first I tried to attached it using WinDBG, re-activated the sample .net console application, but nothing happens. Not even any indication of the loading of .NET Framework specific DLL such as MSCORWKS.DLL in the debugee, when I activated the .net sample console application.

This makes me wondering whether my .NET Framework v2.0 actually checks for certain profiling specific environment variable or not. Because when it is not, then all my works in MyProfile.dll will be in vain. My application will never get hooked, and I have to resort to other paradigm to achieve my goal.

So, to clarify the above statement, I tried to trap the KERNEL32!GetEnvironmentVariable, for evidence Cor_Enable_Profiling environment variable inquiry :

bp 7c80f19f “j ((poi(poi(ebp+8))==0x006f0043)&(poi(poi(ebp+8)+4)==0x005f0072)) ”;’gc'”

And yes, it hit the above conditional break-point. This indicates that my .NET Framework still implements the ICorProfilerInfo profiling mechanism.

There should be some kind of error occured in the profiling routine in the CLR Framework, and indeed it is indicated by viewing the Application Event that says :

“.NET Runtime version 2.0.50727.3603 – Failed to CoCreate profiler.”

By tracing to the exact routines after the Cor_Enable_Profiling is successfully retrieved, I came across this statement that will lead to the above failure in event viewer :

mscorwks!EEDllMain+0x2ff:
7a004dbe ff500c call dword ptr [eax+0Ch] ds:0023:63e71384={mscordbc!EEToProfInterfaceImpl::CreateProfiler (63e7384a)}eax=80070002 ebx=00000004 ecx=63e73972 edx=00000002 esi=00000000 edi=0012fe00
eip=7a004dc1 esp=0012fdd8 ebp=0012fe14 iopl=0 nv up ei ng nz na po nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00000282

You can see that the returned value at eax register is 0x80070002 which is File Not Found Error, and the function is located at mscordbc!EEToProfInterfaceImpl::CreateProfiler.

By analyzing the existing source code of SSCLI v2.0, I’ve verified that this call is actually part of EEToProfInterface’s class method which is HRESULT CreateProfiler(WCHAR *wszCLSID, WCHAR *wszProfileDLL). You can see that this function accepts 2 parameter, one for GUID and one for the DLL file location for our profiling routine.

How come that CreateProfiler results in 0x80070002 or File Not Found Error ? Let’s examine the two parameters that get passed to this method inside the one of mscorwks’s CLR routine :

7a004dad ff75d4 push dword ptr [ebp-2Ch] ss:0023:0012fde8=00000000 ;;?? wszProfileDLL should have some value here
7a004db0 8975e8 mov dword ptr [ebp-18h],esi ss:0023:0012fdfc=00000001
7a004db3 ff75dc push dword ptr [ebp-24h] ss:0023:0012fdf0=0016b098 ;;ok, wszCLSID is successfully retrieved
7a004db6 891d58323b7a mov dword ptr [mscorwks!g_profStatus (7a3b3258)],ebx ds:0023:7a3b3258=00000000
7a004dbc 8b01 mov eax,dword ptr [ecx] ds:0023:00183c48={mscordbc!EEToProfInterfaceImpl::`vftable’ (63e71378)}
7a004dbe ff500c call dword ptr [eax+0Ch] ds:0023:63e71384=63e7384a ;;CreateProfiler

Notice the instruction at 0x7a004dad that supposed to pass the value of wszProfileDLL which should contains the pointer to our profiler DLL file but instead, contains only NULL value.

Now the question is why the wszProfileDLL still contains the null value, albeit that I already stated the environment variable COR_PROFILER_DLL that points to my profiling DLL ?

It turns out that .NET Framework v2.0 don’t bother checking this environment variable at all, but instead using the registry method to access the DLL inside it’s mscordbc!FakeCoCreateInstance routine.

As the name implied, because I also do not bother to create the routine to register the COM server to the registry, I just manually create a fake registry entry, for my dnprofiler COM server :

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\CLSID\{9AB84088-18E7-42F0-8F8D-E022AE3C4517}]
@=”MyProfile.Profiler.1″

[HKEY_CLASSES_ROOT\CLSID\{9AB84088-18E7-42F0-8F8D-E022AE3C4517}\InprocServer32]
@=”D:\\Projects\\MyProfile\\Debug\\MyProfile.DLL”
“ThreadingModel”=”Apartment”

[HKEY_CLASSES_ROOT\CLSID\{9AB84088-18E7-42F0-8F8D-E022AE3C4517}\ProgID]
@=”MyProfile.Profiler.1″

The above statement is saved to *.reg file, and after executing this file, I am effectively performs the regsvr32 manually.

I just can’t believe it that the dnprofiler inside SSCLI v1.0 do not contain necessary source code for COM register routine inside its DllRegisterServer export routine. This routine is necessary so that when I tried to regsvr32-ing the DLL, it can be completed successfully rather than resorting to ugly manual approach as above.

Now with this COM server information placed in the registry, I again activated my .NET Framework application to be profiled, but still do not find the *.out file in the application directory.

In the event viewer, I still have the same error ! Because I already know the exact location of the error-causing routine, I perform the breakpoint at this instruction pointer :

7a004dbe ff500c call dword ptr [eax+0Ch] ds:0023:63e71384={mscordbc!EEToProfInterfaceImpl::CreateProfiler (63e7384a)}

And try to examine the return value at eax register :

eax=8007007f

Now, instead of 0x80070002, the error code is 0x8007007f. The detail information of this error code can be obtained using !error command from WinDBG :

0:000> !error 8007007f
Error code: (HRESULT) 0x8007007f (2147942527) – The specified procedure could not be found.

Things become interesting now, because it seems that somehow my profiling DLL is successfully activated but certain procedure is missing. Let’s find out what kind of procedure the .NET Framework v2.0 demands :

63e78b31 686817e763 push offset mscordbc!`string’ (63e71768)

Examing the data at location 0x63e71768 reveals that the missing procedure is DllGetClassObject. But this is impossible because my profiling DLL already contains this routine :

STDAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, LPVOID FAR *ppv )

After some examination, it is caused by non existent .def file to declare the export routines in MyProfile project.

In the end, I still encounter the 0x80040005 error, but after checking the event viewer :

“.NET Runtime version 2.0.50727.3603 – CLR 2.0 does not support profilers written for CLR 1.x”

I should now accept the fact that different version requires different profile, and because the version is currently do not become issue at this time, I can re-compile my sample .NET project using .NET v1.1.

And dnprofiler.out is indeed resides the sample application directory :

For anyone interested to use my version of CLR profile, don’t hesitate to contact me via email, because it seems I can’t attach the zip file in this blog unless I renamed it to document file extention such as PDF, etc, which is rather unearthly things to do 🙂

Advertisements

3 Responses to “How to Create Working CLR Profiler”

  1. net profiler Says:

    Hi there,)
    can u please send me the source i wanna have
    a closer look
    thank you

  2. Mehdi Says:

    Hi
    Could you please send me your code too?

    Thanks in advance

  3. Jossy Jose Says:

    Hi, I am looking for a utility which can list the method calls. I could see yours fits the bill. Can you pls share the code and instructions as i am not that hands on .Net

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


%d bloggers like this: