<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/atom.xsl" media="screen"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-US"><title type="html">David Broman's CLR Profiling API Blog</title><subtitle type="html">Info about the Common Language Runtime's Profiling API</subtitle><id>http://blogs.msdn.com/davbr/atom.xml</id><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/default.aspx" /><link rel="self" type="application/atom+xml" href="http://blogs.msdn.com/davbr/atom.xml" /><generator uri="http://communityserver.org" version="2.1.61025.2">Community Server</generator><updated>2007-12-06T09:40:00Z</updated><entry><title>CLR V4: Profiler Attach Basics With Sample Code</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2009/11/04/clr-v4-profiler-attach-basics-with-sample-code.aspx" /><id>http://blogs.msdn.com/davbr/archive/2009/11/04/clr-v4-profiler-attach-basics-with-sample-code.aspx</id><published>2009-11-04T17:43:00Z</published><updated>2009-11-04T17:43:00Z</updated><content type="html">&lt;P&gt;A new feature in CLR V4 is the ability to attach a profiler to a process after that process has already started.&amp;nbsp; The usefulness of this is fairly obvious to anyone who's ever attached a debugger to a running-process: It's helpful when diagnosing hard-to-reproduce problems, and particularly useful when encountering issues in production.&lt;/P&gt;
&lt;P&gt;Please note!&amp;nbsp; You can't just take any profiler you bought and suddenly be able to attach it to a running application.&amp;nbsp; The profiler must be built with "attachability" in mind.&amp;nbsp; So if you're a profiler developer looking to pump some attachability into your product, read on--this article is for you.&amp;nbsp; Everyone else, this article will probably be less useful--but just as riveting.&lt;/P&gt;
&lt;H1&gt;&lt;/H1&gt;
&lt;H1&gt;The Players&lt;/H1&gt;
&lt;P&gt;So how do you get your profiler attached to a running process?&amp;nbsp; The process has already started, and the CLR code which interrogates the environment to determine whether to load a profiler has already run.&amp;nbsp; So how do you kick the process into loading your profiler?&amp;nbsp; The answer: Another process!&lt;/P&gt;
&lt;P&gt;&lt;IMG height=275 src="http://blogs.msdn.com/photos/davbr/images/9778066/500x275.aspx" width=500 mce_src="http://blogs.msdn.com/photos/davbr/images/9778066/500x275.aspx"&gt; &lt;/P&gt;
&lt;P&gt;In order to force your profiler DLL to load into the target profilee process, you'll need to create a "trigger" process to send the attach message to the target profilee.&amp;nbsp; Many profilers already ship with a GUI shell to control launching processes to profile.&amp;nbsp; That shell will typically act as your trigger process as well.&lt;/P&gt;
&lt;H1&gt;Inside the Trigger Process&lt;/H1&gt;
&lt;P&gt;Your trigger uses a simple API method, AttachProfiler, to request the target process to load your profiler.&amp;nbsp; Where is this method defined?&amp;nbsp; Well, it doesn't make much sense to put it on ICorProfilerInfo, since that interface is only available to a profiler after it's been loaded.&amp;nbsp; You could imagine a C export from mscoree.dll.&amp;nbsp; But because of in-process side-by-side CLR instances, we're moving away from mscoree.dll exports to a COM-based interface model called "metahost".&amp;nbsp; &lt;/P&gt;
&lt;H2&gt;Meta-whos-its?&lt;/H2&gt;
&lt;P&gt;Whereas the "hosting" interfaces enable one to host and manage a CLR in a process, the "metahost" interfaces allow one to manage multiple CLRs that may be installed onto a machine or loaded into a single process.&amp;nbsp; Here's a high-level view of how you navigate your way through metahost to find AttachProfiler() (there’s a pointer to actual sample code below).&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;Get ICLRMetaHost &lt;/LI&gt;
&lt;LI&gt;Enumerate the CLRs loaded into the target process &lt;/LI&gt;
&lt;LI&gt;Get ICLRRuntimeInfo for the particular CLR in the target process you want to profile &lt;/LI&gt;
&lt;LI&gt;Get the corresponding ICLRProfiling &lt;/LI&gt;
&lt;LI&gt;Call ICLRProfiling::AttachProfiler &lt;/LI&gt;&lt;/UL&gt;
&lt;H2&gt;Users and Integrity&lt;/H2&gt;
&lt;P&gt;The permissions required to attach a profiler are similar to those required to attach a debugger.&amp;nbsp; First, the trigger process must run as the same user as the target profilee OR as an administrator.&amp;nbsp; Second, on OS's that support process integrity levels, the trigger process must be running at an integrity level higher than or equal to that of the target profilee process.&amp;nbsp; For more information about integrity and mandatory labels, &lt;A href="http://msdn.microsoft.com/en-us/library/bb625964.aspx" mce_href="http://msdn.microsoft.com/en-us/library/bb625964.aspx"&gt;here's&lt;/A&gt; some reference from MSDN. &lt;/P&gt;
&lt;H2&gt;Sample Trigger Source Code&lt;/H2&gt;
&lt;P&gt;For some sample code to attach a profiler to a process, take a look at the sample uploaded to the MSDN Code Gallery &lt;A href="http://code.msdn.microsoft.com/ProfilerAttacher/" mce_href="http://code.msdn.microsoft.com/ProfilerAttacher/"&gt;here&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;You'll notice the code attempts to enable the SE_DEBUG_NAME privilege, as this is required to open a process running as another user with PROCESS_ALL_ACCESS.&amp;nbsp; Again, cross-user attach (i.e., trigger runs as a different user than the target profilee process) is only supported when the trigger is run as an administrator.&amp;nbsp; Otherwise, only same-user attach is supported, and would not need to enable the SE_DEBUG_NAME privilege.&lt;/P&gt;
&lt;H1&gt;Inside the Profilee Process&lt;/H1&gt;
&lt;P&gt;Once your trigger has called AttachProfiler(), a message is sent to the target profilee process to load your actual profiler DLL, containing info such as your profiler's GUID.&amp;nbsp; At this point, it's business as usual in the profilee.&amp;nbsp; The profilee locates and loads your profiler DLL, the CLR then calls your class factory object to create an instance of your profiler's ICorProfilerCallback implementation. &lt;/P&gt;
&lt;P&gt;Note that, instead of the CLR calling your Initialize() method, the CLR will call your ICorProfilerCallback3::InitializeForAttach() method.&amp;nbsp; There are two reasons for this difference.&amp;nbsp; First, this ensures that only profilers that have been upgraded to work with CLR V4 and opt into attaching will actually be attached.&amp;nbsp; All other profilers can simply return an error from their InitializeForAttach() method (or won't have an ICorProfilerCallback3 implementation to begin with).&lt;/P&gt;
&lt;P&gt;The second reason is that InitializeForAttach allows for some extra data to be passed from the trigger to your profilee via a blob of binary data.&amp;nbsp; What's that for?&amp;nbsp; Many profiler products pass configuration information from their shell to their startup-loaded profiler DLL via environment variables.&amp;nbsp; After all, the shell has to set COR_PROFILER &amp;amp; COR_ENABLE_PROFILING in the environment anyway, so why not set some more values there to be read by their profiler?&amp;nbsp; This scheme doesn't work for an attaching profiler, since the shell cannot affect the environment of the already-running profilee.&amp;nbsp; Instead, the AttachProfiler API allows the caller to specify a pointer to a buffer containing whatever data the caller wishes.&amp;nbsp; The CLR makes a copy of the data, sends it to the profilee, and then passes a pointer to this data to the profiler DLL via InitializeForAttach.&lt;/P&gt;
&lt;P&gt;The management of the memory containing this binary data follows the usual COM rules.&amp;nbsp; In the trigger process, your trigger code allocates memory for the blob, passes it to AttachProfiler (which will make its own copy of the data), and then your trigger code frees it once AttachProfiler returns.&amp;nbsp; Stack allocation is perfect here; your trigger could just push your own custom structure of data onto the stack and pass a pointer to it in your call to AttachProfiler.&amp;nbsp; Inside the profilee process, your profiler gets access to the blob of data from its InitializeForAttach method.&amp;nbsp; Inside InitializeForAttach, your profiler accesses that memory.&amp;nbsp; If your profiler will need to use that memory later on, your profiler should make a copy of the memory now.&amp;nbsp; After InitializeForAttach returns, the CLR will free the memory. &lt;/P&gt;
&lt;P&gt;From your InitializeForAttach implementation, your profiler will call SetEventMask as usual to announce your intentions, and you're off to the races.&lt;/P&gt;
&lt;H1&gt;Limitations&lt;/H1&gt;
&lt;P&gt;It was impossible to enable all profiling scenarios for attach in the time we had for the V4 release.&amp;nbsp; So only profilers that do &lt;STRONG&gt;sampling &lt;/STRONG&gt;and &lt;STRONG&gt;memory &lt;/STRONG&gt;analysis will function properly after attaching to a live process.&amp;nbsp; Attempts to use other profiling APIs after attach will be met with CORPROF_E_UNSUPPORTED_FOR_ATTACHING_PROFILER.&lt;/P&gt;
&lt;H3&gt;&lt;/H3&gt;
&lt;H2&gt;Specific Callback Limitations&lt;/H2&gt;
&lt;P&gt;When your attaching profiler calls SetEventMask, you will be limited to only those event mask flags present in the COR_PRF_ALLOWABLE_AFTER_ATTACH bitmask (you'll find it in corprof.idl).&amp;nbsp; Any other flags, and SetEventMask will return CORPROF_E_UNSUPPORTED_FOR_ATTACHING_PROFILER.&lt;/P&gt;
&lt;H2&gt;Specific Info Limitations&lt;/H2&gt;
&lt;P&gt;Most of the ICorProfilerInfo* methods are available to your attaching profiler, however some are not--particularly those involved in &lt;STRONG&gt;IL rewriting&lt;/STRONG&gt;.&amp;nbsp; Here's a list of all ICorProfilerInfo* methods NOT supported for attaching profilers:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;GetILFunctionBody &lt;/LI&gt;
&lt;LI&gt;GetILFunctionBodyAllocator &lt;/LI&gt;
&lt;LI&gt;SetILFunctionBody &lt;/LI&gt;
&lt;LI&gt;SetILInstrumentedCodeMap &lt;/LI&gt;
&lt;LI&gt;SetEnterLeaveFunctionHooks* &lt;/LI&gt;
&lt;LI&gt;SetFunctionIDMapper* &lt;/LI&gt;
&lt;LI&gt;GetNotifiedExceptionClauseInfo &lt;/LI&gt;
&lt;LI&gt;All methods related to Enter/Leave/Tailcall &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;It's expected that future releases of the CLR will enable more API methods for use by attaching profilers.&lt;/P&gt;
&lt;H2&gt;GC Limitations&lt;/H2&gt;
&lt;H3&gt;GC Modes&lt;/H3&gt;
&lt;P&gt;To understand limitations around the GC modes, here's a quick review of the GC modes an app can run under:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;&lt;STRONG&gt;Workstation Blocking mode&lt;/STRONG&gt;.&amp;nbsp; The thread that triggered the GC performs the GC while all other threads executing managed code must wait. &lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Workstation Concurrent / Background mode (the default)&lt;/STRONG&gt;.&amp;nbsp; Concurrent GC (V1 &amp;amp; V2) allows portions of a full GC to execute while other threads are allowed to run.&amp;nbsp; Background GC (its replacement in V4) takes it one step further, and also allows an ephemeral GC (i.e., gen 0 or gen 1) to execute while a gen 2 GC is executing. &lt;/LI&gt;
&lt;LI&gt;&lt;STRONG&gt;Server mode&lt;/STRONG&gt;.&amp;nbsp; Hosts like ASP.NET may choose to enable server mode which creates a heap + dedicated GC thread per CPU.&amp;nbsp; This allows GCs to be fanned out to multiple threads. &lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;Of course, &lt;A href="http://blogs.msdn.com/maoni/" mce_href="http://blogs.msdn.com/maoni/"&gt;Maoni's blog&lt;/A&gt; is required reading for anyone who wants to understand how the GC works.&lt;/P&gt;
&lt;P&gt;The profiling API is able to work against workstation blocking mode and server mode, but not concurrent / background mode.&amp;nbsp; This has been the case in V1 &amp;amp; V2, and remains the case in V4.&amp;nbsp; When the app starts up, if a profiler is configured to load, then the CLR forcibly turns off concurrent / background mode, and you end up in workstation blocking mode (or you end up in server mode if the host requested that instead).&amp;nbsp; Again, this has been the case in V1 &amp;amp; V2, and remains true in V4.&lt;/P&gt;
&lt;P&gt;So here's the catch.&amp;nbsp; What if a V4 app starts up in background GC mode &lt;EM&gt;without&lt;/EM&gt; a profiler loading on startup, and you later attach a profiler to the process?&amp;nbsp; If the profiler specifies COR_PRF_MONITOR_GC in its call to SetEventMask, then the CLR returns the error CORPROF_E_CONCURRENT_GC_NOT_PROFILABLE.&amp;nbsp; In other words, if the profiler is late to the party, then it simply won't work if background GC is on.&amp;nbsp; Since this is the default for client apps, the bottom line is that you can generally successfully attach your memory profiler to server apps (e.g., ASP.NET), but probably not to client apps.&lt;/P&gt;
&lt;P&gt;Of course, you could forcibly turn off concurrent / background mode every time the app starts up via a config file:&lt;/P&gt;
&lt;TABLE class="" border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class=""&gt;
&lt;P&gt;&amp;lt;configuration&amp;gt; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;runtime&amp;gt; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;gcConcurrent enabled="false"/&amp;gt; &lt;BR&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/runtime&amp;gt; &lt;BR&gt;&amp;lt;/configuration&amp;gt; &lt;/P&gt;&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;But you don't really want to be running your apps with a sub-optimal GC mode all the time, just on the off-chance you might need to attach a memory profiler to it.&amp;nbsp; If you suspect you might need to do some memory profiling of a client app, you should just start up your app with the memory profiler to begin with.&lt;/P&gt;
&lt;H3&gt;ObjectAllocated&lt;/H3&gt;
&lt;P&gt;The ObjectAllocated callback is disallowed for attaching profilers (i.e., COR_PRF_ENABLE_OBJECT_ALLOCATED is not part of the COR_PRF_ALLOWABLE_AFTER_ATTACH mask).&lt;/P&gt;
&lt;H1&gt;Go Forth and Attach&lt;/H1&gt;
&lt;P&gt;All right, dig through that sample trigger code, and see if you can add "attach" to your list of features.&amp;nbsp; In later posts, I'll talk about how to catch up on application state once your profiler attaches, and also how to detach your profiler when it's done with its business.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9917430" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>CLR V4 Beta 2 Released!</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2009/10/19/clr-v4-beta-2-released.aspx" /><id>http://blogs.msdn.com/davbr/archive/2009/10/19/clr-v4-beta-2-released.aspx</id><published>2009-10-19T17:45:00Z</published><updated>2009-10-19T17:45:00Z</updated><content type="html">&lt;P&gt;All you profiler writers will want to try out your profiler on the latest and greatest!&lt;/P&gt;
&lt;P&gt;Information on getting beta 2 of CLR V4 and Visual Studio 2010 is available &lt;A class="" title=here href="http://msdn.microsoft.com/en-us/vstudio/dd582936.aspx" mce_href="http://msdn.microsoft.com/en-us/vstudio/dd582936.aspx"&gt;here&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;The beta 2 docs for the profiling API start &lt;A class="" title=here href="http://msdn.microsoft.com/en-us/library/ms404386(VS.100).aspx" mce_href="http://msdn.microsoft.com/en-us/library/ms404386(VS.100).aspx"&gt;here&lt;/A&gt;.&lt;/P&gt;
&lt;P&gt;There have been some bug fixes between beta 1 &amp;amp; beta 2 that may improve life for your profiler.&amp;nbsp; It's also worth noting that, for those of you dabbling with getting your profiler to attach to a running process, the method to get an instance of the metahost API has been renamed (since beta 1) to &lt;A class="" title=CLRCreateInstance href="http://msdn.microsoft.com/en-us/library/dd537633(VS.100).aspx" mce_href="http://msdn.microsoft.com/en-us/library/dd537633(VS.100).aspx"&gt;CLRCreateInstance&lt;/A&gt;.&amp;nbsp; If you're looking for more information on how to make your profiler attachable, I will be posting an entry on that as soon as I can.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9909270" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>Type Forwarding</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2009/09/30/type-forwarding.aspx" /><id>http://blogs.msdn.com/davbr/archive/2009/09/30/type-forwarding.aspx</id><published>2009-10-01T03:33:00Z</published><updated>2009-10-01T03:33:00Z</updated><content type="html">&lt;p&gt;MSDN defines “type forwarding” as moving “a type to another assembly without having to recompile applications that use the original assembly”.&amp;#160; In this post, I’ll talk about examining a particular type in Microsoft’s .NET Framework library that gets forwarded, how you can use type forwarding for your own types, and what type forwarding looks like to consumers of the profiling API.&amp;#160; For some more official background on type forwarding, visit the MSDN &lt;a href="http://msdn.microsoft.com/en-us/library/ms404275(VS.100).aspx"&gt;topic&lt;/a&gt;.&amp;#160; If you Bing type forwarding you’ll find many blogs that talk about it as well.&amp;#160; Yes, that’s right.&amp;#160; I used Bing as a verb.&amp;#160; Get used to it; Bing is awesome.&lt;/p&gt;  &lt;p&gt;Type forwarding is nothing new.&amp;#160; However, in CLR V4, we are enabling type forwarding to work with generic types.&amp;#160; And there has been some new refactoring in System.Core.&amp;#160; This means you should expect to see type forwarding used more often than it had been in the past.&amp;#160; So if you code up a profiler, you should make sure you can deal with type forwarding appropriately.&amp;#160; The good news is that profiler code that uses the profiling API to inspect types generally should not need to change.&amp;#160; But if you do certain kinds of metadata lookups yourself, you may need to be aware of type forwarding.&amp;#160; More on that later.&lt;/p&gt;  &lt;h2&gt;Example: TimeZoneInfo&lt;/h2&gt;  &lt;p&gt;The example I’ll use where the .NET Framework uses type forwarding is the TimeZoneInfo class.&amp;#160; In CLR V4, TimeZoneInfo is now forwarded from System.Core.dll to mscorlib.dll.&amp;#160; If you open the CLR V4 copy of System.Core.dll in ildasm and choose Dump, you'll see the following:&lt;/p&gt;  &lt;table border="1"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td&gt;         &lt;pre&gt;.class extern /*27000004*/ forwarder System.TimeZoneInfo
{
  .assembly extern mscorlib /*23000001*/ 
}&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;In each assembly’s metadata is an exported types table.&amp;#160; The above means that System.Core.dll's exported types table includes an entry for System.TimeZoneInfo (indexed by token 27000004).&amp;#160; What's significant is that System.Core.dll no longer has a typeDef for System.TimeZoneInfo, only an exported type.&amp;#160; The fact that the token begins at the left with 0x27 tells you that it's an mdtExportedType (not a mdtTypeDef, which begins at the left with 0x02).&lt;/p&gt;

&lt;p&gt;At run-time, if the CLR type loader encounters this exported type, it knows it must now look in mscorlib for System.TimeZoneInfo.&amp;#160; And by the way, if someday mscorlib chooses to forward the type elsewhere, and thus the type loader found another exported type with name System.TimeZoneInfo in mscorlib, then the type loader would have to make yet another hop to wherever that exported type pointed.&lt;/p&gt;

&lt;h2&gt;Walkthrough 1: Observe the forwarding of System.TimeZoneInfo&lt;/h2&gt;

&lt;p&gt;This walkthrough assumes you have .NET 4.0 Beta 1 installed (see &lt;a href="http://blogs.msdn.com/davbr/archive/2009/05/26/clr-v4-beta-1-released.aspx"&gt;here&lt;/a&gt;) &lt;strong&gt;and&lt;/strong&gt; an older release of .NET, such as .NET 3.5, installed.&lt;/p&gt;

&lt;p&gt;Code up a simple C# app that uses System.TimeZoneInfo:&lt;/p&gt;

&lt;p&gt;&lt;span style="color: rgb(0,0,255)"&gt;namespace&lt;/span&gt; test 

  &lt;br /&gt;{ 

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Class1 
    &lt;br /&gt;&lt;/span&gt;&amp;#160;&amp;#160;&amp;#160; { 

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; Main(&lt;span style="color: rgb(0,0,255)"&gt;string&lt;/span&gt;[] args) 

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; { 

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; System.TimeZoneInfo ti = &lt;span style="color: rgb(0,0,255)"&gt;null&lt;/span&gt;; 

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160;&amp;#160; } 

  &lt;br /&gt;&amp;#160;&amp;#160;&amp;#160; } 

  &lt;br /&gt;}&lt;/p&gt;

&lt;p&gt;Next, compile this into an exe using a CLR V2-based toolset (e.g., .NET 3.5).&amp;#160; You can use Visual Studio, or just run from the command-line (but be sure your path points to the pre-.NET 4.0 C# compiler!).&amp;#160; Example:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;csc /debug+ /o- /r:&amp;quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Core.dll&amp;quot; Class1.cs&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Again, be sure you’re using an old csc.exe from, say, a NET 3.5 installation.&amp;#160; To verify, open up Class1.exe in ildasm, and take a look at Main().&amp;#160; It should look something like this:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;.method /*06000001*/ private hidebysig static 
        void  Main(string[] args) cil managed
{
  .entrypoint
  // Code size       4 (0x4)
  .maxstack  1
&lt;strong&gt;  .locals /*11000001*/ init ([0] class [&lt;font color="#ff0080"&gt;&lt;span style="background-color: #ffff00"&gt;&lt;font color="#ff0080"&gt;System.Core&lt;/font&gt;&lt;/span&gt;&lt;/font&gt;/*23000002*/]System.TimeZoneInfo/*01000006*/ ti)&lt;/strong&gt;
  IL_0000:  nop
  IL_0001:  ldnull
  IL_0002:  stloc.0
  IL_0003:  ret
} // end of method Class1::Main&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;The key here is to note that the IL uses a TypeRef for System.TimeZoneInfo (01000006) that points to &lt;strong&gt;System.Core.dll&lt;/strong&gt;.&amp;#160; When you run Class1.exe against a .NET 3.5 runtime, it will find System.TimeZoneInfo in System.Core.dll as usual, and just use that, since System.TimeZoneInfo actually is defined in System.Core.dll in pre-.NET 4.0 frameworks.&amp;#160; However, what happens when you run Class1.exe against .NET 4.0 without recompiling?&amp;#160; Type forwarding would get invoked!&lt;/p&gt;

&lt;p&gt;Note that, if you were to build the above C# code using the .NET 4.0 C# compiler, it would automatically have generated a TypeRef that points to mscorlib.dll instead, so you wouldn't be able to observe the type forwarding at run-time.&lt;/p&gt;

&lt;p&gt;Ok, so how do we run this pre-.NET 4.0 executable against .NET 4.0?&amp;#160; A config file, of course.&amp;#160; Paste the following into a file named Class1.exe.config that sits next to Class1.exe:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;&amp;lt;configuration&amp;gt;
   &amp;lt;startup&amp;gt;
      &amp;lt;supportedRuntime version=&amp;quot;v4.0.20506&amp;quot;/&amp;gt;
   &amp;lt;/startup&amp;gt;
&amp;lt;/configuration&amp;gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;The above will force Class1.exe to bind against .NET 4.0 Beta 1.&amp;#160; And when it comes time to look for TimeZoneInfo, the CLR will first look in System.Core.dll, find the exported types table entry, and then hop over to mscorlib.dll to load the type.&amp;#160; What does that look like to your profiler?&amp;#160; Make your guess and hold that thought.&amp;#160; First, another walkthrough…&lt;/p&gt;

&lt;h2&gt;Walkthrough 2: Forwarding your own type&lt;/h2&gt;

&lt;p&gt;To experiment with forwarding your own types, the process is:&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;Create Version 1 of your library 
    &lt;ul&gt;
      &lt;li&gt;Create version 1 of your library assembly that defines your type (MyLibAssemblyA.dll) &lt;/li&gt;

      &lt;li&gt;Create an app that references your type in MyLibAssemblyA.dll (MyClient.exe) &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;

  &lt;li&gt;Create version 2 of your library 
    &lt;ul&gt;
      &lt;li&gt;Recompile MyLibAssemblyA.dll to forward your type elsewhere (MyLibAssemblyB.dll) &lt;/li&gt;

      &lt;li&gt;Don’t recompile MyClient.exe.&amp;#160; Let it still think the type is defined in MyLibAssemblyA.dll. &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;Version 1&lt;/h3&gt;

&lt;p&gt;Just make a simple C# DLL that includes your type Foo.&amp;#160; Something like this (MyLibAssemblyA.cs):&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System;
&lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Foo
&lt;/span&gt;{
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;and compile it into MyLibAssemblyA.dll:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;csc /target:library /debug+ /o- MyLibAssemblyA.cs&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Then make yourself a client app that references Foo.&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System;
&lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Test
&lt;/span&gt;{
    &lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;static&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;void&lt;/span&gt; Main()
    {
        Foo foo = &lt;span style="color: rgb(0,0,255)"&gt;new&lt;/span&gt; Foo();
        &lt;span style="color: rgb(43,145,175)"&gt;Console&lt;/span&gt;.WriteLine(&lt;span style="color: rgb(0,0,255)"&gt;typeof&lt;/span&gt;(Foo).AssemblyQualifiedName);
    }
}&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;and compile this into MyClient.exe:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;csc /debug+ /o- /r:MyLibAssemblyA.dll MyClient.cs&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;When you run MyClient.exe, you get this boring output:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;Foo, MyLibAssemblyA, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Ok, time to upgrade!&lt;/p&gt;

&lt;h3&gt;Version 2&lt;/h3&gt;

&lt;p&gt;Time goes by, your library is growing, and its time to split it into two DLLs.&amp;#160; Gotta move Foo into the new DLL.&amp;#160; Save this into MyLibAssemblyB.cs&lt;/p&gt;

&lt;p&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System; 

  &lt;br /&gt;&lt;span style="color: rgb(0,0,255)"&gt;public&lt;/span&gt; &lt;span style="color: rgb(0,0,255)"&gt;class&lt;/span&gt; &lt;span style="color: rgb(43,145,175)"&gt;Foo 
    &lt;br /&gt;&lt;/span&gt;{ 

  &lt;br /&gt;}&lt;/p&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;compile that into your new DLL, MyLibAssemblyB.dll:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;csc /target:library /debug+ /o- MyLibAssemblyB.cs&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;And for the type forward.&amp;#160; MyLibAssemblyA.cs now becomes:&lt;/p&gt;

&lt;pre class="code"&gt;&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System;
&lt;span style="color: rgb(0,0,255)"&gt;using&lt;/span&gt; System.Runtime.CompilerServices;
[&lt;span style="color: rgb(0,0,255)"&gt;assembly&lt;/span&gt;: &lt;span style="color: rgb(43,145,175)"&gt;TypeForwardedTo&lt;/span&gt;(&lt;span style="color: rgb(0,0,255)"&gt;typeof&lt;/span&gt;(Foo))]&lt;/pre&gt;
&lt;a href="http://11011.net/software/vspaste"&gt;&lt;/a&gt;

&lt;p&gt;compile that into MyLibAssemblyA.dll (overwriting your Version 1 copy of that DLL):&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;csc /target:library /debug+ /o- /r:MyLibAssemblyB.dll MyLibAssemblyA.cs&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Now, when you rerun MyClient.exe (without recompiling!), it will look for Foo first in MyLibAssemblyA.dll, and then hop over to MyLibAssemblyB.dll:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;Foo, &lt;span style="background-color: #ffff00"&gt;MyLibAssemblyB&lt;/span&gt;, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;And this all despite the fact that MyClient.exe still believes that Foo lives in MyLibAssemblyA:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;.method /*06000001*/ public hidebysig static 
        void  Main() cil managed
{
  .entrypoint
  // Code size       29 (0x1d)
  .maxstack  1
  .locals /*11000001*/ init ([0] class [MyLibAssemblyA/*23000002*/]Foo/*01000006*/ foo)
  IL_0000:  nop
  IL_0001:  newobj     instance void [MyLibAssemblyA/*23000002*/]Foo/*01000006*/::.ctor() /* 0A000004 */
  IL_0006:  stloc.0
 &lt;strong&gt; IL_0007:  ldtoken    [&lt;span style="background-color: #ffff00"&gt;MyLibAssemblyA&lt;/span&gt;/*23000002*/]Foo/*01000006*/&lt;/strong&gt;
  IL_000c:  call       class [mscorlib/*23000001*/]System.Type/*01000007*/ [mscorlib/*23000001*/]System.Type/*01000007*/::GetTypeFromHandle(valuetype [mscorlib/*23000001*/]System.RuntimeTypeHandle/*01000008*/) /* 0A000005 */
  IL_0011:  callvirt   instance string [mscorlib/*23000001*/]System.Type/*01000007*/::get_AssemblyQualifiedName() /* 0A000006 */
  IL_0016:  call       void [mscorlib/*23000001*/]System.Console/*01000009*/::WriteLine(string) /* 0A000007 */
  IL_001b:  nop
  IL_001c:  ret
} // end of method Test::Main&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;h2&gt;Profilers&lt;/h2&gt;

&lt;p&gt;What does this look like to profilers?&amp;#160; Types are represented as ClassIDs, and modules as ModuleIDs.&amp;#160; When you query for info about a ClassID (via GetClassIDInfo2()), you get one and only one ModuleID to which it belongs.&amp;#160; So when a ClassID gets forwarded from one ModuleID to another, which does the profiling API report as its real home?&amp;#160; The answer: always the final module to which the type has been forwarded and therefore the module whose metadata contains the TypeDef (and not the exported type table entry).&lt;/p&gt;

&lt;p&gt;This should make life easy for profilers, since they generally expect to be able to find the metadata TypeDef for a type inside the ModuleID that the profiling API claims is the type’s home.&amp;#160; So much of type forwarding will be transparent to your profiler.&lt;/p&gt;

&lt;p&gt;However, type forwarding is important to understand if your profiler needs to follow metadata references directly.&amp;#160; More generally, if your profiler is reading through metadata and expects to come across a typeDef (e.g., perhaps a metadata reference points to a type in that module, or perhaps your profiler expects certain known types to be in certain modules), then your profiler should be prepared to find an mdtExportedType instead, and to deal gracefully with it rather than doing something silly like crashing.&lt;/p&gt;

&lt;p&gt;In any case, whether you think your profiler will be affected by type forwarding, be sure to test, test, test!&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9901597" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>CLR V4: Load your profiler without using the registry</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2009/06/16/clr-v4-load-your-profiler-without-using-the-registry.aspx" /><id>http://blogs.msdn.com/davbr/archive/2009/06/16/clr-v4-load-your-profiler-without-using-the-registry.aspx</id><published>2009-06-16T23:36:23Z</published><updated>2009-06-16T23:36:23Z</updated><content type="html">&lt;p&gt;One of the new features in CLR V4 is the ability to load your profiler without needing to register it first.&amp;#160; In V2, we would look at the following environment variables:&lt;/p&gt;  &lt;p&gt;COR_ENABLE_PROFILING=1&lt;/p&gt;  &lt;p&gt;COR_PROFILER={&lt;em&gt;CLSID of profiler&lt;/em&gt;}&lt;/p&gt;  &lt;p&gt;and look up the CLSID from COR_PROFILER in the registry to find the full path to your profiler's DLL.&amp;#160; Just like with any COM server DLL, we look for your profiler's CLSID under HKEY_CLASSES_ROOT, which merges the classes from HKLM and HKCU.&lt;/p&gt;  &lt;p&gt;We mostly follow the same algorithm in V4, so you can continue registering your profiler if you wish.&amp;#160; However, in V4 we look for one more environment variable first:&lt;/p&gt;  &lt;p&gt;COR_PROFILER_PATH=&lt;em&gt;full path to your profiler's DLL&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;If that environment variable is present, we skip the registry look up altogether, and just use the path from COR_PROFILER_PATH to load your DLL.&amp;#160; A couple things to note about this:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;COR_PROFILER_PATH is purely optional.&amp;#160; If you don't specify COR_PROFILER_PATH, we use the old procedure of looking up your profiler's CLSID in the registry to find its path&lt;/li&gt;    &lt;li&gt;If you specify COR_PROFILER_PATH &lt;em&gt;and&lt;/em&gt; register your profiler, then COR_PROFILER_PATH always wins.&amp;#160; Even if COR_PROFILER_PATH points to an invalid path, we will still use COR_PROFILER_PATH, and just fail to load your profiler.&lt;/li&gt;    &lt;li&gt;COR_PROFILER is &lt;em&gt;always required&lt;/em&gt;.&amp;#160; If you specify COR_PROFILER_PATH, we skip the registry look up; however, we still need to know your profiler's CLSID, so we can pass it to your class factory's CreateInstance call.&lt;/li&gt; &lt;/ul&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9763071" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>What does Dave look like?</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2009/05/28/what-does-dave-look-like.aspx" /><id>http://blogs.msdn.com/davbr/archive/2009/05/28/what-does-dave-look-like.aspx</id><published>2009-05-28T20:59:29Z</published><updated>2009-05-28T20:59:29Z</updated><content type="html">&lt;p&gt;Find out on channel 9 as Jon Langdon, Thomas Lai, and I &lt;a href="http://channel9.msdn.com/posts/Charles/CLR-4-Debugging-and-Profiling-Enhancements/"&gt;discuss&lt;/a&gt; some of the new diagnostics features in CLR V4.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9648266" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>Run your V2 profiler binary on CLR V4</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2009/05/26/run-your-v2-profiler-binary-on-clr-v4.aspx" /><id>http://blogs.msdn.com/davbr/archive/2009/05/26/run-your-v2-profiler-binary-on-clr-v4.aspx</id><published>2009-05-27T04:38:27Z</published><updated>2009-05-27T04:38:27Z</updated><content type="html">&lt;p&gt;Ok, you've installed VS 2010 beta 1, along with .NET FX 4.0 beta 1, and you're wondering--can you run your profiler against this new .NET framework without recompiling the profiler?&lt;/p&gt;  &lt;p&gt;Yes, you can!&amp;#160; Though not by default.&amp;#160; Although CLR V4 is much more compatible with CLR V2 than CLR V2 was with CLR V1.1, there are still some differences that can mess with your profiler's mind.&amp;#160; So by default, the CLR V4 runtime refuses to activate V2 profilers.&amp;#160; If you try, you'll see that the CLR will LoadLibrary the profiler, use the class factory to generate an instance of the callback object, try to QI for the new ICorProfilerCallback3, and then when that fails, the CLR will release the callback object and log an event to the event log explaining that you need to set COMPLUS_ProfAPI_ProfilerCompatibilitySetting appropriately as your way of &amp;quot;opting in&amp;quot; to running the older profiler binary against the newer CLR.&amp;#160; This message is meant for your users, as a way of telling them that the profiler vendor (you) may not yet have tested the profiler against CLR V4.&lt;/p&gt;  &lt;p&gt;So, how to use COMPLUS_ProfAPI_ProfilerCompatibilitySetting?&amp;#160; Set it to one of the following 3 values:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;EnableV2Profiler&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;It enables the V2 profiler to be activated by V4 CLR.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;DisableV2Profiler (default)&lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;V4 CLR refuses to activate the V2 profiler, and logs an event to the event log.&lt;/li&gt;   &lt;/ul&gt;    &lt;li&gt;PreventLoad &lt;/li&gt;    &lt;ul&gt;     &lt;li&gt;V4 CLR does not load the profiler, regardless of the profiler&amp;#8217;s version.&amp;#160; This is useful for preventing problems in certain in-process side-by-side CLR scenarios.&amp;#160; More on that in an upcoming post.&lt;/li&gt;   &lt;/ul&gt; &lt;/ul&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9643160" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>CLR V4 Beta 1 Released!</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2009/05/26/clr-v4-beta-1-released.aspx" /><id>http://blogs.msdn.com/davbr/archive/2009/05/26/clr-v4-beta-1-released.aspx</id><published>2009-05-27T04:23:54Z</published><updated>2009-05-27T04:23:54Z</updated><content type="html">&lt;p&gt;Now is the time to try out your profiler against the new .NET FX 4.0 Beta 1 bits.&amp;#160; I'll be writing about some gotchas, and how to take advantage of the new features.&amp;#160; But first, get started downloading:&lt;/p&gt;  &lt;p&gt;&lt;a title="Visual Studio 2010 Product Page" href="http://www.microsoft.com/visualstudio/products/2010/default.mspx"&gt;Visual Studio 2010 Product Page&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;You can find some reference documentation on the new profiling interfaces here:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://msdn.microsoft.com/en-us/library/ms404386(VS.100).aspx" href="http://msdn.microsoft.com/en-us/library/ms404386(VS.100).aspx"&gt;http://msdn.microsoft.com/en-us/library/ms404386(VS.100).aspx&lt;/a&gt;&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9643108" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>FunctionHooks.zip re-uploaded</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2009/02/14/functionhooks-zip-re-uploaded.aspx" /><id>http://blogs.msdn.com/davbr/archive/2009/02/14/functionhooks-zip-re-uploaded.aspx</id><published>2009-02-15T01:45:00Z</published><updated>2009-02-15T01:45:00Z</updated><content type="html">&lt;P&gt;Jonathan Keljo's &lt;A class="" href="http://blogs.msdn.com/jkeljo/archive/2005/08/11/450506.aspx" mce_href="http://blogs.msdn.com/jkeljo/archive/2005/08/11/450506.aspx"&gt;blog entry&lt;/A&gt; on the enter/leave/tailcall function hooks had a link to sample code that's been broken for a while.&amp;nbsp; You can now find the sample code here: &lt;A class="" href="http://feeblah.members.winisp.net/direct/blog/FunctionHooks.zip" mce_href="http://feeblah.members.winisp.net/direct/blog/FunctionHooks.zip"&gt;FunctionHooks.zip&lt;/A&gt;&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9422797" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>Why we have CORPROF_E_UNSUPPORTED_CALL_SEQUENCE</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2008/12/23/why-we-have-corprof-e-unsupported-call-sequence.aspx" /><id>http://blogs.msdn.com/davbr/archive/2008/12/23/why-we-have-corprof-e-unsupported-call-sequence.aspx</id><published>2008-12-23T21:08:30Z</published><updated>2008-12-23T21:08:30Z</updated><content type="html">&lt;p&gt;&lt;em&gt;What follows is a long-lost blog entry that &lt;/em&gt;&lt;a href="http://blogs.msdn.com/jkeljo"&gt;&lt;em&gt;Jonathan Keljo&lt;/em&gt;&lt;/a&gt;&lt;em&gt; had been working on.&amp;#160; I brushed off some of the dust and am posting it here for your enjoyment.&amp;#160; Thank you, Jonathan!&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;In CLR 2.0 we added a new HRESULT, CORPROF_E_UNSUPPORTED_CALL_SEQUENCE.&amp;#160; This HRESULT is returned from ICorProfilerInfo methods when called in an &amp;quot;unsupported way&amp;quot;.&amp;#160; This &amp;quot;unsupported way&amp;quot; is primarily an issue with those nasty beasts, hijacking profilers (though read on for cases where non-hijacking profilers can see this HRESULT, too).&amp;#160; Hijacking profilers are those profilers that forcibly reset a thread's register context at completely arbitrary times to enter profiler code, and then usually to re-enter the CLR via ICorProfilerInfo.&amp;#160; Why is that so bad?&amp;#160; Well, for the sake of performance, lots of the IDs the profiling API gives out are just pointers to relevant data structures within the CLR. So lots of ICorProfilerInfo calls just rip information out of those data structures and pass them back. Of course, the CLR might be changing things in those structures as it runs, maybe (or maybe not) taking locks to do so.&amp;#160; Imagine the CLR was already holding (or attempting to acquire) such locks at the time the profiler hijacked the thread.&amp;#160; Now, the thread re-enters the CLR, trying to take more locks or inspect structures that were in the process of being modified, and are thus in an inconsistent state.&amp;#160; Deadlocks and AVs are easy to come by in such situations.&lt;/p&gt;  &lt;p&gt;In general, if you're a non-hijacking profiler sitting inside an ICorProfilerCallback method and you're calling into ICorProfilerInfo, you're fine. For example, you get a ClassLoadFinished and you start asking for information about the class. You might be told that information isn't available yet (CORPROF_E_DATAINCOMPLETE) but the program won't deadlock or AV.&amp;#160; This class of calls into ICorProfilerInfo are called &amp;quot;synchronous&amp;quot;, because they are made from within an ICorProfilerCallback method.&lt;/p&gt;  &lt;p&gt;On the other hand, if you're hijacking or otherwise calling ICorProfilerInfo functions on a managed thread but &lt;strong&gt;not&lt;/strong&gt; from within an ICorProfilerCallback method, that is considered an &amp;quot;asynchronous&amp;quot; call.&amp;#160; In v1.x you never knew what would happen in an asynchronous call. It might deadlock, it might crash, it might give a bogus answer, or it might give the right answer.&lt;/p&gt;  &lt;p&gt;In 2.0 we've added some simple checks to help you avoid this problem. If you call an unsafe ICorProfilerInfo function asynchronously, instead of crossing its fingers and trying, it will fail with CORPROF_E_UNSUPPORTED_CALL_SEQUENCE.&amp;#160; The general rule of thumb is, nothing is safe to call asynchronously.&amp;#160; But here are the exceptions that are safe, and that we specifically allow to be called asynchronously:&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;GetEventMask/SetEventMask &lt;/li&gt;    &lt;li&gt;GetCurrentThreadID &lt;/li&gt;    &lt;li&gt;GetThreadContext &lt;/li&gt;    &lt;li&gt;GetThreadAppDomain &lt;/li&gt;    &lt;li&gt;GetFunctionFromIP &lt;/li&gt;    &lt;li&gt;GetFunctionInfo/GetFunctionInfo2 &lt;/li&gt;    &lt;li&gt;GetCodeInfo/GetCodeInfo2 &lt;/li&gt;    &lt;li&gt;GetModuleInfo &lt;/li&gt;    &lt;li&gt;GetClassIDInfo/GetClassIDInfo2 &lt;/li&gt;    &lt;li&gt;IsArrayClass &lt;/li&gt;    &lt;li&gt;ForceGC &lt;/li&gt;    &lt;li&gt;SetFunctionIDMapper &lt;/li&gt;    &lt;li&gt;DoStackSnapshot &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;There are also a few things to keep in mind:&lt;/p&gt;  &lt;ol&gt;   &lt;li&gt;ICorProfilerInfo calls made from within the fast-path Enter/Leave callbacks are considered asynchronous.&amp;#160; (Though ICorProfilerInfo calls made from within the &lt;em&gt;slow&lt;/em&gt;-path Enter/Leave callbacks are considered synchronous.)&amp;#160; See the blog entries &lt;a href="http://blogs.msdn.com/davbr/archive/2007/03/22/enter-leave-tailcall-hooks-part-1-basics.aspx"&gt;here&lt;/a&gt; and &lt;a href="http://blogs.msdn.com/jkeljo/archive/2005/08/11/450506.aspx"&gt;here&lt;/a&gt; for more info on fast / slow path. &lt;/li&gt;    &lt;li&gt;ICorProfilerInfo calls made from within instrumented code (i.e., IL you've rewritten to call into your profiler and then into ICorProfilerInfo) are considered asynchronous. &lt;/li&gt;    &lt;li&gt;Calls made inside your FunctionIDMapper hook are considered to be synchronous. &lt;/li&gt;    &lt;li&gt;Calls made on threads created by your profiler, are always considered to be synchronous.&amp;#160; (This is because there's no danger of conflicts resulting from interrupting and then re-entering the CLR on that thread, since a profiler-created thread was not in the CLR to begin with.) &lt;/li&gt;    &lt;li&gt;Calls made inside a StackSnapshotCallback are considered to be synchronous iff the call to DoStackSnapshot was synchronous.      &lt;br /&gt;&lt;/li&gt; &lt;/ol&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9250517" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>New stuff in Profiling API for upcoming CLR 4.0</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2008/11/10/new-stuff-in-profiling-api-for-upcoming-clr-4-0.aspx" /><id>http://blogs.msdn.com/davbr/archive/2008/11/10/new-stuff-in-profiling-api-for-upcoming-clr-4-0.aspx</id><published>2008-11-11T02:05:18Z</published><updated>2008-11-11T02:05:18Z</updated><content type="html">&lt;p&gt;Now that we've finally announced at PDC many of the new features coming up in the next major release of Visual Studio and CLR, I can elaborate some on what's coming up for the profiling API.&amp;#160; Also, see Rick Byers's blog &lt;a href="http://blogs.msdn.com/rmbyers/archive/2008/10/30/clr-4-0-advancements-in-diagnostics.aspx"&gt;entry&lt;/a&gt; which also talks about debugging improvements.&lt;/p&gt;  &lt;h2&gt;What the CLR will do for you&lt;/h2&gt;  &lt;p&gt;Our upcoming profiling API-specific features are inspired by a vision to improve production troubleshooting, and happily such features will improve the developer desktop experience as well.&lt;/p&gt;  &lt;h5&gt;Attach / detach&lt;/h5&gt;  &lt;p&gt;We will now allow profilers to attach to and detach from managed processes that are already running.&amp;#160; You no longer need to set environment variables and load the profiler when the managed app starts up.&amp;#160; (However, if you would like your profiler to load when the process starts up, you would still use the existing activation mechanism with environment variables.)&amp;#160; Attach / detach works with only a limited set of scenarios--see below.&lt;/p&gt;  &lt;p&gt;You initiate the attach from a separate executable that we call the &amp;quot;trigger process&amp;quot;.&amp;#160; If you already have a shell for your profiler, then your shell will typically serve as the trigger process.&amp;#160; We will provide you an API that your trigger process will call, which takes parameters that describe the target app to profile and details about your profiler.&amp;#160; That API will cause the target app to load your profiler into the target app's process space using the same code that we currently use to load your profiler from startup.&lt;/p&gt;  &lt;p&gt;When your profiler is ready to detach from the process, it calls a method right on ICorProfilerInfo3 (the new Info interface for CLR 4.0).&amp;#160; That will cause the CLR to stop issuing profiling callbacks, and slip a bit until the profiler is provably off of all the threads' call stacks.&amp;#160; The profiler will then be unloaded from the process space, and another profiler may be attached again if the end user wishes.&lt;/p&gt;  &lt;p&gt;Due to the nature of the profiling APIs, only a subset of the APIs will be available to profilers that attach to a live process, as opposed to those profilers that load on startup.&amp;#160; Specifically, attaching profilers will be able to use the APIs that enable &lt;strong&gt;sampling&lt;/strong&gt; and &lt;strong&gt;memory profiling&lt;/strong&gt;.&amp;#160; This includes such operations as walking the stack, mapping instruction pointers to managed methods and their metadata, receiving most GC callbacks, and inspecting statics and object instances on the heap, along with their type information. &lt;/p&gt;  &lt;p&gt;APIs that will &lt;em&gt;not &lt;/em&gt;be supported for attaching profilers include the ObjectAllocated callback and APIs that enable instrumentation (IL rewriting) of methods.&amp;#160; Those scenarios require the ability to &amp;quot;rejit&amp;quot; a method that has already been JITted or loaded from an NGENd module, and unfortunately we are unable to provide that functionality in CLR 4.&lt;/p&gt;  &lt;p&gt;&lt;em&gt;&amp;quot;Still no rejit?!&amp;#160; Are you kidding me?&amp;quot;&lt;/em&gt;&lt;/p&gt;  &lt;p&gt;I wish I were.&amp;#160; Given customer demand, I personally rate rejit as more important than most of the other profiling API features we're doing combined.&amp;#160; However, rejit is very expensive in terms of development and testing.&amp;#160; After doing the math, it became apparent that even if we cut most of the other new profiling API features for CLR 4.0, rejit would still not fit.&amp;#160; But given the high demand, we're certainly still looking to deliver rejit in a future release of the CLR, just not 4.0.&lt;/p&gt;  &lt;h5&gt;Registry-free activation&lt;/h5&gt;  &lt;p&gt;An obstacle to getting profilers installed into production data centers is that operations managers distrust &amp;quot;impactful&amp;quot; installations that modify machine state.&amp;#160; So we're providing a way where you no longer need to register your profiler when it's installed or used.&amp;#160; (I'm talking here about the COM registration that uses the Windows registry to map your profiler's CLSID to the full path to the profiler's DLL.)&amp;#160; It also turns out that, even in developer desktop scenarios, relying on the registry can be a common cause of failures.&amp;#160; For example, perhaps your profiler tries to regsvr32 itself under HKLM, but the user does not have administrative privileges.&amp;#160; Or maybe the user does have administrative privileges, but is using Vista in non-elevated mode.&amp;#160; So registry-free activation should help with all of those scenarios.&amp;#160; Note that registry-free activation is optional.&amp;#160; Your profiler may continue to use traditional COM registration if you like.&lt;/p&gt;  &lt;h5&gt;Profiler Backward Compatibility&lt;/h5&gt;  &lt;p&gt;CLR 2.0 saw significant enough changes from CLR 1.1 that we refused to load 1.1 profilers into CLR 2.0.&amp;#160; However, CLR 4.0 is compatible enough with CLR 2.x that we will allow 2.x profilers to load into CLR 4.0 applications.&amp;#160; This behavior would not be the default, however.&amp;#160; If end users try to load their 2.x profiler into a CLR 4.0 application, the load will fail, and they will see an event log entry telling them either to upgrade their profiler, or to set a special environment variable to explicitly allow the older profiler to load.&amp;#160; By forcing the end users to opt in to this behavior, we set the expectation that it is not guaranteed or tested that 2.x profilers will still work, though we believe it is likely they will work in many scenarios.&lt;/p&gt;  &lt;h5&gt;Enter/Leave/Tailcall Enhancements&lt;/h5&gt;  &lt;p&gt;We have made some enhancements to the Enter/Leave/Tailcall interface to cut down on overhead when your profiler does not care about getting parameter or return value information.&amp;#160; &lt;/p&gt;  &lt;h5&gt;Other random stuff&lt;/h5&gt;  &lt;p&gt;You will also find several minor enhancements and bug fixes to the profiling API.&amp;#160; Not worth listing them all out here, but the forthcoming documentation on ICorProfilerCallback3 / ICorProfilerInfo3 will describe them.&lt;/p&gt;  &lt;h2&gt;What you must do for the CLR&lt;/h2&gt;  &lt;p&gt;Here are some of your responsibilities for playing nicely with CLR 4.0 applications.&lt;/p&gt;  &lt;h5&gt;In-process side-by-side CLR instances&lt;/h5&gt;  &lt;p&gt;Probably the biggest impact to your profiler as you upgrade it to CLR 4.0 will be supporting in-process side-by-side CLR instances.&amp;#160; This is actually a CLR-wide feature for 4.0 (not profiling API), but it has impact on profiling API tools.&amp;#160; Certain scenarios will now result in multiple instances of the CLR loaded into a single process, primarily to support backward-compatibility for managed components that load into a host.&amp;#160; (Imagine one old (2.x) CLR instance alongside a new (4.0) CLR instance in the same process.)&amp;#160; From the profiler&amp;#8217;s point of view, it will be loaded multiple times, once per CLR instance.&amp;#160; This means your DLL gets LoadLibrary&amp;#8217;d multiple times and you&amp;#8217;ll receive multiple &amp;#8220;CreateInstance&amp;#8221; calls to your class factory object, to generate multiple instances of your ICorProfilerCallback implementation.&amp;#160; You can deal with this by: &lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;Returning failure from all but one of your CreateInstance() calls.&amp;#160; This allows you to &amp;#8220;pick&amp;#8221; which CLR instance you wish to interact with.&amp;#160; OR &lt;/li&gt;    &lt;li&gt;Succeeding many or all of your CreateInstance() calls.&amp;#160; This allows you to examine multiple CLRs simultaneously. &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Pick 1 / Pick first&lt;/u&gt;&lt;/strong&gt;: With this first approach, your profiler will choose to return success from only one CreateInstance() call.&amp;#160; &amp;quot;Pick 1&amp;quot; implies you allow your user to specify &amp;quot;which&amp;quot; CLR to profile, usually specified in terms of the version number of the CLR of interest.&amp;#160; &amp;quot;Pick First&amp;quot; implies you don't even ask your user--you just simplistically succeed the first CreateInstance() call, and fail the rest.&amp;#160; First CLR wins.&amp;#160; The advantages of these approaches are they are fairly easy to implement, and either one qualifies your profiler as being &amp;quot;side-by-side aware&amp;quot;.&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;&lt;u&gt;Pick many / Pick all&lt;/u&gt;&lt;/strong&gt;: With this approach, your profiler collects data on multiple CLRs, with the intent of presenting that data to the user in some unified way.&amp;#160; You will have to be careful to manage multiple instances of your ICorProfilerCallback implementation and probably eliminate much of your global state.&amp;#160; For example, if you call into an ICorProfilerInfo from one CLR with IDs (e.g., AppDomainID) of the other CLR, that will likely cause an AV.&amp;#160; Many profilers are implemented with a global pointer to the &amp;quot;one and only&amp;quot; instance of their ICorProfilerCallback implementation.&amp;#160; This would no longer work, as you will now have multiple instances of your ICorProfilerCallback implementation, and each one must keep track of the corresponding ICorProfilerInfo interface to call into.&amp;#160; There will be enhancements to the profiling API to make this management easier, most notably improvements to the Enter/Leave/Tailcall and FunctionIDMapper interfaces.&lt;/p&gt;  &lt;p&gt;I cannot stress the following enough, so I will state it twice.&amp;#160; &lt;strong&gt;When you update your profiler to work with CLR 4.0, you &lt;em&gt;must&lt;/em&gt; update your profiler to become side-by-side aware.&lt;/strong&gt;&amp;#160; This means you must do some amount of work, even if it is the simple &amp;quot;pick first&amp;quot; approach.&amp;#160; The CLR determines whether your profiler is &amp;quot;updated for CLR 4.0&amp;quot; by QI'ing for the new ICorProfilerCallback3 defined in the CLR 4.0 corprofl.IDL file.&amp;#160; If your profiler successfully returns a pointer to your ICorProfilerCallback3 implementation, then your profiler is considered a 4.0 profiler.&amp;#160; So to restate: &lt;strong&gt;If your profiler provides an ICorProfilerCallback3 implementation, then your profiler must be side-by-side aware&lt;/strong&gt;.&amp;#160; The reason for this rule is that the CLR puts certain safeguards in place to protect older (2.x) profilers when they might load into scenarios that involve in-process side-by-side CLR instances.&amp;#160; If you claim your profiler is updated for 4.0, those safeguards are lifted, and you really don't want that to happen unless you're side-by-side aware.&lt;/p&gt;  &lt;p&gt;If you're curious to learn more about this &amp;quot;in-process side-by-side CLR instances&amp;quot; feature, unfortunately the blogs and documentation are still pretty thin for the moment (though I imagine that will change in the coming months).&amp;#160; You can take a look at the PDC talk on CLR futures, which discussed this feature at a high level.&amp;#160; Go to the PDC 2008 &lt;a href="https://sessions.microsoftpdc.com/public/timeline.aspx"&gt;site&lt;/a&gt;, and find the session called &amp;quot;PC49 Microsoft .NET Framework: CLR Futures&amp;quot;.&lt;/p&gt;  &lt;h5&gt;Profiler Backward Compatibility&lt;/h5&gt;  &lt;p&gt;Not much extra to state here, but just to be explicit, you of course have a choice.&amp;#160; If you have a profiler that works just fine against CLR 2.0, you may either update it to work with CLR 4.0, or &lt;em&gt;not &lt;/em&gt;update it.&amp;#160; If you choose to update it, that means you must implement ICorProfilerCallback3 and provide that implementation to the CLR when the CLR QI's for ICorProfilerCallback3.&amp;#160; And, due to the contract stated above, you must also ensure your profiler is side-by-side aware (pick 1, pick first, pick many, pick all, it's up to you).&amp;#160; The alternative is, don't update your profiler!&amp;#160; You will miss out on the new profiling API features listed above.&amp;#160; And your profiler may also not work well in scenarios that load in-process side-by-side CLR instances.&amp;#160; But maybe you don't care, or maybe you just want a temporary stopgap for your users until you've had the time to update and test your profiler for CLR 4.0.&amp;#160; Just remember that CLR 4.0 will not activate your 2.x profiler by default.&amp;#160; You will need to tell your users about the special environment variable mentioned above to get your 2.0 profiler to load into CLR 4.0.&amp;#160; Since this is all new stuff, the environment variable has not yet been documented at the time of this blog entry, but you can expect more info on it in MSDN when we release, and possibly info on this blog sooner.&lt;/p&gt;  &lt;p&gt;&amp;#160;&lt;/p&gt;  &lt;p&gt;I hope you've found this overview useful.&amp;#160; Since this is new and not documented yet, there's not much you can do to start preparing for CLR 4.0 yet.&amp;#160; However, I'd recommend you take a look through your code and see what it would take to become side-by-side aware.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=9058865" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>Visual Studio 2008 SP1 and .NET Framework 3.5 SP1 Released</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2008/08/13/visual-studio-2008-sp1-and-net-framework-3-5-sp1-released.aspx" /><id>http://blogs.msdn.com/davbr/archive/2008/08/13/visual-studio-2008-sp1-and-net-framework-3-5-sp1-released.aspx</id><published>2008-08-14T00:21:18Z</published><updated>2008-08-14T00:21:18Z</updated><content type="html">&lt;p&gt;See Soma's &lt;a href="http://blogs.msdn.com/somasegar/archive/2008/08/11/service-pack-1-for-vs-2008-and-net-fx-3-5-released.aspx"&gt;blog entry&lt;/a&gt; for more information.&amp;#160; Also, I updated the table that maps Visual Studio versions, .NET Framework versions, and CLR versions &lt;a href="http://blogs.msdn.com/davbr/archive/2007/12/06/versions-of-microsoft-net-framework-clr-and-your-profiler.aspx"&gt;here&lt;/a&gt;.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8861109" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>BUG: GetILFunctionBody returns wrong size</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2008/05/22/bug-getilfunctionbody-returns-wrong-size.aspx" /><id>http://blogs.msdn.com/davbr/archive/2008/05/22/bug-getilfunctionbody-returns-wrong-size.aspx</id><published>2008-05-23T02:26:30Z</published><updated>2008-05-23T02:26:30Z</updated><content type="html">&lt;p&gt;In case you missed it, there was a post on our forum here:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1366051&amp;amp;SiteID=1" href="http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1366051&amp;amp;SiteID=1"&gt;http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1366051&amp;amp;SiteID=1&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;and a comment on my blog here:&lt;/p&gt;  &lt;p&gt;&lt;a title="http://blogs.msdn.com/davbr/archive/2007/03/06/creating-an-il-rewriting-profiler.aspx#1881536" href="http://blogs.msdn.com/davbr/archive/2007/03/06/creating-an-il-rewriting-profiler.aspx#1881536"&gt;http://blogs.msdn.com/davbr/archive/2007/03/06/creating-an-il-rewriting-profiler.aspx#1881536&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;about an issue with GetILFunctionBody returning the wrong size.&amp;#160; This is indeed a bug in CLR 2.x, and it is recommended you follow the ECMA spec help you parse the function header to determine the actual size.&amp;#160; More details are in the forum post linked above.&amp;#160; This bug will be fixed in a future release of the CLR.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=8535188" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>Debugging Your Profiler II: SOS and IDs</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2007/12/18/debugging-your-profiler-ii-sos-and-ids.aspx" /><id>http://blogs.msdn.com/davbr/archive/2007/12/18/debugging-your-profiler-ii-sos-and-ids.aspx</id><published>2007-12-19T00:14:13Z</published><updated>2007-12-19T00:14:13Z</updated><content type="html">&lt;p&gt;In this debugging post, I'll talk about the various IDs the profiling API exposes to your profiler, and how you can use SOS to give you more information about the IDs.&amp;#160; As usual, this post assumes you're using CLR 2.x.&lt;/p&gt;  &lt;h2&gt;S.O.What Now?&lt;/h2&gt;  &lt;p&gt;SOS.DLL is a debugger extension DLL that ships with the CLR.&amp;#160; You'll find it sitting alongside mscorwks.dll.&amp;#160; While originally written as an extension to the windbg family of debuggers, Visual Studio can also load and use SOS.&amp;#160; If you search the MSDN blogs for &amp;quot;SOS&amp;quot; you'll find lots of info on it.&amp;#160; I'm not going to repeat all that's out there, but I'll give you a quick primer on getting it loaded.&lt;/p&gt;  &lt;p&gt;In windbg, you'll need mscorwks.dll to load first, and then you can load SOS.&amp;#160; Often, I don't need SOS until well into my debugging session, at which point mscorwks.dll has already been loaded anyway.&amp;#160; However, there are some cases where you'd like SOS loaded at the first possible moment, so you can use some of its commands early (like !bpmd to set a breakpoint on a managed method).&amp;#160; So a surefire way to get SOS loaded ASAP is to have the debugger break when mscorwks gets loaded (e.g., &amp;quot;sxe ld mscorwks&amp;quot;).&amp;#160; Once mscorwks is loaded, you can load SOS using the .loadby command:&lt;/p&gt;  &lt;table border="1"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td&gt;         &lt;pre&gt;0:000&amp;gt; &lt;strong&gt;&lt;font color="#008000"&gt;sxe ld mscorwks&lt;/font&gt;&lt;/strong&gt;
0:000&amp;gt; g
ModLoad: 79e70000 7a3ff000   C:\Windows\Microsoft.NET\Framework\v2.0.50727\mscorwks.dll
eax=00000000 ebx=00000000 ecx=00000000 edx=00000000 esi=7efdd000 edi=20000000
eip=77a1a9fa esp=002fea38 ebp=002fea78 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
ntdll!NtMapViewOfSection+0x12:
77a1a9fa c22800          ret     28h
0:000&amp;gt; &lt;strong&gt;&lt;font color="#008000"&gt;.loadby sos mscorwks&lt;/font&gt;&lt;/strong&gt;&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;With SOS loaded, you can now use its commands to inspect the various IDs that the profiling API passes to your profiler.&lt;/p&gt;

&lt;p&gt;&lt;span style="background-color: #ffff00"&gt;&lt;font size="3"&gt;Note: The following contains implementation details of the runtime.&amp;#160; While these details are useful as a debugging aid, your profiler code cannot make assumptions about them.&amp;#160; These implementation details are subject to change at whim.&lt;/font&gt;&lt;/span&gt;&lt;/p&gt;

&lt;h2&gt;FunctionID Walkthrough&lt;/h2&gt;

&lt;p&gt;For starters, take a look at FunctionIDs.&amp;#160; Your profiler receives a FunctionID anytime you hit a callback that needs to, well, identify a function!&amp;#160; For example, when it's time to JIT, the CLR issues JITCompilationStarted (assuming your profiler subscribed to that callback), and one of the parameters to the callback is a FunctionID.&amp;#160; You can then use that FunctionID in later calls your profiler makes back into the CLR, such as GetFunctionInfo2.&lt;/p&gt;

&lt;p&gt;As far as your profiler is concerned, a FunctionID is just an opaque number.&amp;#160; It has no meaning in itself; it's merely a handle you can pass back into the CLR to refer to the function.&amp;#160; Under the covers, however, a FunctionID is actually a pointer to an internal CLR data structure called a MethodDesc.&amp;#160; I must warn you again that you cannot rely on this information when coding your profiler.&amp;#160; The CLR team reserves the right to change the underlying meaning of a FunctionID to be something radically different in later versions.&amp;#160; This info is for entertainment and debugging purposes only!&lt;/p&gt;

&lt;p&gt;Ok, so FunctionID = (MethodDesc *).&amp;#160; How does that help you?&amp;#160; SOS just so happens to have a command to inspect MethodDescs: !dumpmd.&amp;#160; So if you're in a debugger looking at your profiler code that's operating on a FunctionID, it can beneficial to you to find out which function that FunctionID actually refers to.&amp;#160; In the example below, the debugger will break in my proifler's JITCompilationStarted callback and look at the FunctionID.&amp;#160; It's assumed that you've already loaded SOS as per above.&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;0:000&amp;gt; bu UnitTestSampleProfiler!SampleCallbackImpl::JITCompilationStarted
0:000&amp;gt; g
...&lt;/pre&gt;

        &lt;pre&gt;Breakpoint 0 hit
eax=00c133f8 ebx=00000000 ecx=10001218 edx=00000001 esi=002fec74 edi=00000000
eip=10003fc0 esp=002fec64 ebp=002feca4 iopl=0         nv up ei pl nz na po nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000202
UnitTestSampleProfiler!SampleCallbackImpl::JITCompilationStarted:
10003fc0 55              push    ebp&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;The debugger is now sitting at the beginning of my profiler's JITCompilationStarted callback.&amp;#160; Let's take a look at the parameters.&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;0:000&amp;gt; dv
           this = 0x00c133f8
     &lt;strong&gt;&lt;font color="#008000"&gt;functionID = 0x1e3170&lt;/font&gt;&lt;/strong&gt;
 fIsSafeToBlock = 1&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Aha, that's the FunctionID about to get JITted.&amp;#160; Now use SOS to see what that function really is.&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;0:000&amp;gt; !dumpmd 0x1e3170
Method Name: test.Class1.Main(System.String[])
Class: 001e1288
&lt;strong&gt;&lt;font color="#008000"&gt;MethodTable: 001e3180
&lt;/font&gt;&lt;/strong&gt;mdToken: 06000001
Module: 001e2d8c
IsJitted: no
m_CodeOrIL: ffffffff&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Lots of juicy info here, though the Method Name typically is what helps me the most in my debugging sessions.&amp;#160; mdToken tells us the metadata token for this method.&amp;#160; MethodTable tells us where another internal CLR data structure is stored that contains information about the class containing the function.&amp;#160; In fact, the profiing API's ClassID is simply a MethodTable *.&amp;#160; [Note: the &amp;quot;Class: 001e1288&amp;quot; in the output above is very different from the MethodTable, and thus different from the profiling API's ClassID.&amp;#160; Don't let the name fool you!]&amp;#160; So we could go and inspect a bit further by dumping information about the MethodTable:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;0:000&amp;gt; !dumpmt 0x001e3180
EEClass: 001e1288
Module: 001e2d8c
Name: test.Class1
mdToken: 02000002  (C:\proj\HelloWorld\Class1.exe)
BaseSize: 0xc
ComponentSize: 0x0
Number of IFaces in IFaceMap: 0
Slots in VTable: 6&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;And of course, !dumpmt can be used anytime you come across a ClassID and want more info on it.&lt;/p&gt;

&lt;h2&gt;IDs and their Dumpers&lt;/h2&gt;

&lt;p&gt;Now that you see how this works, you'll need to know how the profiling IDs relate to the various SOS commands that dump info on them:&lt;/p&gt;

&lt;table cellspacing="0" cellpadding="2" width="633" border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td valign="top" width="135"&gt;&lt;strong&gt;ID&lt;/strong&gt;&lt;/td&gt;

      &lt;td valign="top" width="285"&gt;&lt;strong&gt;Internal CLR Structure&lt;/strong&gt;&lt;/td&gt;

      &lt;td valign="top" width="206"&gt;&lt;strong&gt;SOS command&lt;/strong&gt;&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="137"&gt;AssemblyID&lt;/td&gt;

      &lt;td valign="top" width="285"&gt;Assembly *&lt;/td&gt;

      &lt;td valign="top" width="206"&gt;!DumpAssembly&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="139"&gt;AppDomainID&lt;/td&gt;

      &lt;td valign="top" width="285"&gt;AppDomain *&lt;/td&gt;

      &lt;td valign="top" width="206"&gt;!DumpDomain&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="141"&gt;ModuleID&lt;/td&gt;

      &lt;td valign="top" width="285"&gt;Module *&lt;/td&gt;

      &lt;td valign="top" width="206"&gt;!DumpModule&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="142"&gt;ClassID&lt;/td&gt;

      &lt;td valign="top" width="285"&gt;MethodTable *&lt;/td&gt;

      &lt;td valign="top" width="206"&gt;!DumpMT&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="143"&gt;ThreadID&lt;/td&gt;

      &lt;td valign="top" width="285"&gt;Thread *&lt;/td&gt;

      &lt;td valign="top" width="206"&gt;!Threads (see note)&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="144"&gt;FunctionID&lt;/td&gt;

      &lt;td valign="top" width="285"&gt;MethodDesc *&lt;/td&gt;

      &lt;td valign="top" width="206"&gt;!DumpMD&lt;/td&gt;
    &lt;/tr&gt;

    &lt;tr&gt;
      &lt;td valign="top" width="144"&gt;ObjectID&lt;/td&gt;

      &lt;td valign="top" width="285"&gt;Object * (i.e., a managed object)&lt;/td&gt;

      &lt;td valign="top" width="206"&gt;!DumpObject&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;Note:&amp;#160; !Threads takes no arguments, but simply dumps info on all threads that have ever run managed code.&amp;#160; If you use &amp;quot;!Threads -special&amp;quot; you get to see other special threads separated out explicitly, including threads that perform GC in server-mode, the finalizer thread, and the debugger helper thread.&lt;/p&gt;

&lt;h2&gt;More Useful SOS Commands&lt;/h2&gt;

&lt;p&gt;It would probably be quicker to list what &lt;em&gt;isn't&lt;/em&gt; useful!&amp;#160; I encourage you to do a !help to see what's included. Here's a sampling of what I commonly use:&lt;/p&gt;

&lt;p&gt;!u is a nice SOS analog to the windbg command &amp;quot;u&amp;quot;. While the latter gives you a no-frills disassembly, !u works nicely for managed code, including spanning the disassembly from start to finish, and converting metadata tokens to names.&lt;/p&gt;

&lt;p&gt;!bpmd lets you place a breakpoint on a managed method. Just specify the module name and the fully-qualified method name. For example:&lt;/p&gt;

&lt;table border="1"&gt;&lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;
        &lt;pre&gt;!bpmd MyModule.exe MyNamespace.MyClass.Foo &lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;If the method hasn't jitted yet, no worries. A &amp;quot;pending&amp;quot; breakpoint is placed.&amp;#160; If your profiler performs IL rewriting, then using !bpmd on startup to set a managed breakpoint can be a handy way to break into the debugger just before your instrumented code will run (which, in turn, is typically just after your instrumented code has been jitted). This can help you in reproducing and diagnosing issues your profiler may run into when instrumenting particular functions (due to something interesting about the signature, generics, etc.).&lt;/p&gt;

&lt;p&gt;!PrintException: If you use this without arguments you get to see a pretty-printing of the last outstanding managed exception on the thread; or specify a particular Exception object's address.&lt;/p&gt;

&lt;p&gt;&amp;#160;&lt;/p&gt;

&lt;p&gt;Ok, that about does it for SOS. Hopefully this info can help you track down problems a little faster, or better yet, perhaps this can help you step through and verify your code before problems arise.&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6799640" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>Debugging Your Profiler I: Activation</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2007/12/11/debugging-your-profiler-i-activation.aspx" /><id>http://blogs.msdn.com/davbr/archive/2007/12/11/debugging-your-profiler-i-activation.aspx</id><published>2007-12-12T04:26:28Z</published><updated>2007-12-12T04:26:28Z</updated><content type="html">&lt;p&gt;This is the first of some tips to help you debug your profiler.&amp;#160; Note that these tips assume you're using CLR 2.x (see &lt;a href="http://blogs.msdn.com/davbr/archive/2007/12/06/versions-of-microsoft-net-framework-clr-and-your-profiler.aspx"&gt;this entry&lt;/a&gt; for info on how CLR version numbers map to .NET Framework version numbers).&amp;#160; In today's post, I address a frequent question from profiler developers and users: &amp;quot;Why didn't my profiler load?&amp;quot;.&lt;/p&gt;  &lt;h2&gt;Event log&lt;/h2&gt;  &lt;p&gt;In the Application event log, you'll see entries if the CLR attempts, but fails, to load and initialize your profiler.&amp;#160; So this is a nice and easy place to look first, as the message may well make it obvious what went wrong.&lt;/p&gt;  &lt;h2&gt;Weak link in the chain?&lt;/h2&gt;  &lt;p&gt;The next step is to carefully retrace this chain to make sure everything is registered properly:&lt;/p&gt;  &lt;p&gt;Environment variables --&amp;gt; Registry --&amp;gt; Profiler DLL on File system.&lt;/p&gt;  &lt;p&gt;The first link in this chain is to check the environment variables inside the process that should be profiled.&amp;#160; If you're running the process from a command-prompt, you can just try a &amp;quot;set co&amp;quot; from the command prompt:&lt;/p&gt;  &lt;table border="1"&gt;&lt;tbody&gt;     &lt;tr&gt;       &lt;td&gt;         &lt;pre&gt;&lt;b&gt;&lt;span style="color: rgb(0,0,160)"&gt;C:\&amp;gt;&lt;/span&gt;&lt;/b&gt;&lt;i&gt;&lt;span style="color: rgb(164,0,0)"&gt;set co&lt;/span&gt;&lt;/i&gt;
(blah blah, other vars beginning with &amp;quot;co&amp;quot;)&lt;/pre&gt;

        &lt;pre&gt;Cor_Enable_Profiling=0x1
COR_PROFILER={C5F90153-B93E-4138-9DB7-EB7156B07C4C}&lt;/pre&gt;
      &lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;&lt;/table&gt;

&lt;p&gt;If your scenario doesn't allow you to just run the process from a command prompt, like say an asp.net scenario, you may want to attach a debugger to the process that's supposed to be profiled, or use IFEO (HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Image File Execution Options) to force a debugger to start when the worker process starts.&amp;#160; In the debugger, you can then use &amp;quot;!peb&amp;quot; to view the environment block, which will include the environment variables.&lt;/p&gt;

&lt;p&gt;Once you verify Cor_Enable_Profiling and COR_PROFILER are ok, it's time to search the registry for the very same GUID set in your COR_PROFILER environment variable.&amp;#160; You should find it at a path like this:&lt;/p&gt;

&lt;p&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{C5F90153-B93E-4138-9DB7-EB7156B07C4C}&lt;/p&gt;

&lt;p&gt;&lt;font color="#800080"&gt;Note!&amp;#160; If you're on a 64 bit box, be aware of the &lt;/font&gt;&lt;a href="http://blogs.msdn.com/davbr/archive/2006/11/13/wow64-and-your-profiler.aspx"&gt;&lt;font color="#800080"&gt;WOW64 redirectors&lt;/font&gt;&lt;/a&gt;&lt;font color="#800080"&gt;, and ensure you're looking at the proper view of the environment and registry!&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;If the registry has the GUID value, it's finally time to check out your file system.&amp;#160; Go under the InprocServer32 subkey under the GUID:&lt;/p&gt;

&lt;p&gt;HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{C5F90153-B93E-4138-9DB7-EB7156B07C4C}\InprocServer32&lt;/p&gt;

&lt;p&gt;and look at the default value data.&amp;#160; It should be a full path to your profiler's DLL.&amp;#160; Verify it's accurate.&amp;#160; If not, perhaps you didn't properly run regsvr32 against your profiler, or maybe your profiler's &lt;strong&gt;DllRegisterServer&lt;/strong&gt; had problems.&lt;/p&gt;

&lt;h2&gt;Time for a debugger&lt;/h2&gt;

&lt;p&gt;If the above investigation indicates everything's ok, then your profiler is properly registered and your environment is properly set up, but something bad must be happening at run time.&amp;#160; You'll want symbols for the CLR, which are freely available via Microsoft's symbol server.&amp;#160; If you set this environment variable, you can ensure windbg will always use the symbol server:&lt;/p&gt;

&lt;p&gt;&lt;font face="Lucida Console"&gt;set _NT_SYMBOL_PATH=srv*C:\MySymbolCache*http://msdl.microsoft.com/download/symbols&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Feel free to add more paths (separate them via &amp;quot;;&amp;quot;) so you can include your profiler's symbols as well.&amp;#160; Now, from a command-prompt that has your Cor_Enable_Profiling and COR_PROFILER variables set, run windbg against the executable you want profiled.&amp;#160; The debuggee will inherit the environment, so the profiling environment variables will be propagated to the debuggee.&lt;/p&gt;

&lt;p&gt;&lt;span style="background-color: #ffff00"&gt;Note: The following contains implementation details of the runtime.&amp;#160; While these details are useful as a debugging aid, your profiler code cannot make assumptions about them.&amp;#160; These implementation details are subject to change at whim.&lt;/span&gt;&lt;/p&gt;

&lt;p&gt;Once windbg is running, try setting this breakpoint:&lt;/p&gt;

&lt;p&gt;&lt;font face="Lucida Console"&gt;bu mscordbc!EEToProfInterfaceImpl::CreateProfiler&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;Now go!&amp;#160; If you hit that breakpoint, that verifies the CLR has determined that a profiler has been requested to load from the environment variables, but the CLR has yet to read the registry.&amp;#160; Let's see if your DLL actually gets loaded.&amp;#160; You can use&lt;/p&gt;

&lt;p&gt;&lt;font face="Lucida Console"&gt;sxe ld &lt;em&gt;NameOfYourProfiler&lt;/em&gt;.dll&lt;/font&gt;&lt;/p&gt;

&lt;p&gt;or even set a breakpoint inside your Profiler DLL's &lt;strong&gt;DllMain.&lt;/strong&gt;&amp;#160; Now go, and see if your profiler is getting loaded.&amp;#160; If you can verify your profiler's DLL is getting loaded, then you now know your registry is pointing to the proper path, and any static dependencies your profiler has on other DLLs have been resolved.&amp;#160; But will your profiler COM object get instantiated properly?&amp;#160; Set breakpoints in your class factory (&lt;strong&gt;DllGetClassObject&lt;/strong&gt;) and your profiler COM object's &lt;strong&gt;QueryInterface&lt;/strong&gt; to see if you can spot problems there.&amp;#160; For example, if your profiler only works against CLR 1.x, then the CLR's call into your QueryInterface will fail, since you don't implement ICorProfilerCallback2.&lt;/p&gt;

&lt;p&gt;If you're still going strong, set a breakpoint in your profiler's &lt;strong&gt;Initialize&lt;/strong&gt;() callback.&amp;#160; Failures here are actually a popular cause for activation problems.&amp;#160; Inside your Initialize() callback, your profiler is likely calling QueryInterface for the ICorProfilerInfoX interface of your choice, and then calling SetEventMask, and doing other initialization-related tasks, like calling SetEnterLeaveFunctionHooks(2).&amp;#160; Do any of these fail?&amp;#160; Is your Initialize() callback returning a failure HRESULT?&lt;/p&gt;

&lt;p&gt;Hopefully by now you've isolated the failure point.&amp;#160; If not, and your Initialize() is happily returning S_OK, then your profiler is apparently loading just fine.&amp;#160; At least it is when you're debugging it.&amp;#160; :-)&lt;/p&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6740782" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry><entry><title>Versions of Microsoft .NET Framework, CLR, and Your Profiler</title><link rel="alternate" type="text/html" href="http://blogs.msdn.com/davbr/archive/2007/12/06/versions-of-microsoft-net-framework-clr-and-your-profiler.aspx" /><id>http://blogs.msdn.com/davbr/archive/2007/12/06/versions-of-microsoft-net-framework-clr-and-your-profiler.aspx</id><published>2007-12-06T20:40:00Z</published><updated>2007-12-06T20:40:00Z</updated><content type="html">&lt;P&gt;[Updated 8/13/2008 with the release of Visual Studio 2008 SP1.]&lt;/P&gt;
&lt;P&gt;With the many releases of the Microsoft .NET Frameworks and their service packs, it might not be obvious what versions of the Common Language Runtime (CLR) come alongside them and whether your profiler should care.&amp;nbsp; So I'm posting this to help clarify the various versions.&lt;/P&gt;
&lt;P&gt;Note: I'm not talking about the Microsoft .NET &lt;STRONG&gt;Compact&lt;/STRONG&gt; Framework or Microsoft .NET &lt;STRONG&gt;Micro &lt;/STRONG&gt;Framework in this post.&lt;/P&gt;
&lt;H2&gt;Check it out!&lt;/H2&gt;
&lt;P&gt;Before I go any further, I invite you to learn more about the recently released Visual Studio 2008 SP1 and Microsoft .NET Framework 3.5 SP1.&amp;nbsp; Some great places to start:&lt;/P&gt;
&lt;H5&gt;Visual Studio 2008 and Microsoft .NET Framework 3.5:&lt;/H5&gt;
&lt;P&gt;&lt;A href="http://weblogs.asp.net/scottgu/archive/2007/11/19/visual-studio-2008-and-net-3-5-released.aspx" mce_href="http://weblogs.asp.net/scottgu/archive/2007/11/19/visual-studio-2008-and-net-3-5-released.aspx"&gt;this post&lt;/A&gt; from ScottGu's blog, &lt;A href="http://blogs.msdn.com/somasegar/archive/2007/11/19/visual-studio-2008-and-net-framework-3-5-shipped.aspx" mce_href="http://blogs.msdn.com/somasegar/archive/2007/11/19/visual-studio-2008-and-net-framework-3-5-shipped.aspx"&gt;this post&lt;/A&gt; from Soma's blog, or directly at the Visual Studio page &lt;A title=http://www.microsoft.com/vstudio href="http://www.microsoft.com/vstudio" mce_href="http://www.microsoft.com/vstudio"&gt;http://www.microsoft.com/vstudio&lt;/A&gt;.&lt;/P&gt;
&lt;H5&gt;Visual Studio 2008 SP1 and Microsoft .NET Framework 3.5 SP1:&lt;/H5&gt;
&lt;P&gt;&lt;A href="http://blogs.msdn.com/somasegar/archive/2008/08/11/service-pack-1-for-vs-2008-and-net-fx-3-5-released.aspx"&gt;this post&lt;/A&gt; from Soma's blog.&lt;/P&gt;
&lt;H2&gt;Definitions&lt;/H2&gt;
&lt;P&gt;Ok, so what's the difference between "Common Language Runtime (CLR)" and "Microsoft .NET Framework"?&amp;nbsp; I think of it as:&lt;/P&gt;
&lt;P&gt;CLR + managed libraries and tools = Microsoft .NET Framework&lt;/P&gt;
&lt;P&gt;The CLR is the low-level technology (much of it written in unmanaged, native code) that includes the garbage collector, security subsystem, just-in-time compiler, type system, the profiling API (of course :-)), and other similar stuff.&amp;nbsp; Much of this tends to reside in mscorwks.dll.&lt;/P&gt;
&lt;P&gt;If you then add onto that list the many rich managed libraries for implementing graphical user interfaces, web services, accessing Windows OS functionality, etc., as well as the managed language compilers and tools, you get the Microsoft .NET Framework.&lt;/P&gt;
&lt;P&gt;&lt;SPAN style="BACKGROUND-COLOR: #ffff00"&gt;Disclaimer: These definitions are how I, &lt;EM&gt;geek-Dave,&lt;/EM&gt; keep things straight in my head.&amp;nbsp; I'm not in marketing so the names I'm using might not be perfectly accurate (e.g., I'm probably missing terms like "SDK" or "redistributable package").&amp;nbsp; Please don't take these as Microsoft Official Definitions.&lt;/SPAN&gt;&lt;/P&gt;
&lt;H2&gt;Versions&lt;/H2&gt;
&lt;P&gt;You can get the various Microsoft .NET Framework versions via Windows Update, by searching on &lt;A href="http://download.microsoft.com/" mce_href="http://download.microsoft.com"&gt;http://download.microsoft.com&lt;/A&gt;, or by installing the corresponding version of Visual Studio.&amp;nbsp; Also, Windows Vista comes with Microsoft .NET Framework 3.0 as an optionally installable component.&lt;/P&gt;
&lt;P&gt;Here's how the Microsoft .NET Framework versions &amp;gt;= 2.0 correlate with the CLR versions:&lt;/P&gt;
&lt;TABLE class="" cellSpacing=0 cellPadding=2 width=577 border=1&gt;
&lt;TBODY&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=162&gt;&lt;STRONG&gt;Microsoft .NET Framework version&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=126&gt;&lt;STRONG&gt;CLR version&lt;/STRONG&gt;&lt;/TD&gt;
&lt;TD class="" vAlign=top width=287&gt;&lt;STRONG&gt;Ships with Visual Studio Version&lt;/STRONG&gt;&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=162&gt;2.0&lt;/TD&gt;
&lt;TD class="" vAlign=top width=126&gt;2.0&lt;/TD&gt;
&lt;TD class="" vAlign=top width=287&gt;2005&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=162&gt;2.0 SP1&lt;/TD&gt;
&lt;TD class="" vAlign=top width=126&gt;2.0 SP1&lt;/TD&gt;
&lt;TD class="" vAlign=top width=287&gt;2008 (via .NET 3.5 install)&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=162&gt;3.0&lt;/TD&gt;
&lt;TD class="" vAlign=top width=126&gt;2.0&lt;/TD&gt;
&lt;TD class="" vAlign=top width=287&gt;(comes with Vista)&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=162&gt;3.0 SP 1&lt;/TD&gt;
&lt;TD class="" vAlign=top width=126&gt;2.0 SP1&lt;/TD&gt;
&lt;TD class="" vAlign=top width=287&gt;2008 (via .NET 3.5 install)&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=162&gt;3.5&lt;/TD&gt;
&lt;TD class="" vAlign=top width=126&gt;2.0 SP1&lt;/TD&gt;
&lt;TD class="" vAlign=top width=287&gt;2008&lt;/TD&gt;&lt;/TR&gt;
&lt;TR&gt;
&lt;TD class="" vAlign=top width=162&gt;3.5 SP 1&lt;/TD&gt;
&lt;TD class="" vAlign=top width=126&gt;2.0 SP2&lt;/TD&gt;
&lt;TD class="" vAlign=top width=287&gt;2008 SP1&lt;/TD&gt;&lt;/TR&gt;&lt;/TBODY&gt;&lt;/TABLE&gt;
&lt;P&gt;Note that the latest CLR version at the time of writing this post is CLR 2.0 SP2 (no such thing as CLR 3.x!).&lt;/P&gt;
&lt;P&gt;One exciting note here is that Visual Studio 2008 will actually let you target all three of .NET 2.0, .NET 3.0, and .NET 3.5.&lt;/P&gt;
&lt;H2&gt;Your Profiler&lt;/H2&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;U&gt;CLR Versions&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Your profiler interacts with the CLR Profiling API, so you likely care which CLR version you're interacting with.&amp;nbsp; Knowing that the .NET Framework 2.x/3.x versions are all based on CLR 2.x simplifies the picture for you, as you're assured that the profiling API remains compatible across those versions.&amp;nbsp; However, it's fair to expect that CLR service packs might bring small changes or bug fixes.&amp;nbsp; CLR 2.0 SP1, for example, contains the following profiling API fixes:&lt;/P&gt;
&lt;UL&gt;
&lt;LI&gt;SetEnterLeaveFunctionHooks2 returned E_INVALIDARG if any of the specified callback pointers were NULL.&amp;nbsp; For example, one might wish to specify an Enter probe (non-NULL) but not specify a Leave probe (NULL); this was disallowed.&amp;nbsp; Fix: SetEnterLeaveFunctionHooks2 now allows one or more of the callback parameters to be NULL. &lt;/LI&gt;
&lt;LI&gt;Various fixes around IMethodMalloc::Alloc &lt;/LI&gt;
&lt;LI&gt;When a profiler was monitoring Leave calls in order to inspect non-primitive, value-type return values, the profiling API would sometimes specify an invalid value.&amp;nbsp; This has been fixed.&amp;nbsp; (More info about this bug was posted &lt;A href="http://blogs.msdn.com/davbr/archive/2006/02/27/540280.aspx" mce_href="http://blogs.msdn.com/davbr/archive/2006/02/27/540280.aspx"&gt;here&lt;/A&gt; and &lt;A href="http://blogs.msdn.com/davbr/archive/2006/06/07/620925.aspx" mce_href="http://blogs.msdn.com/davbr/archive/2006/06/07/620925.aspx"&gt;here&lt;/A&gt;.) &lt;A href="http://11011.net/software/vspaste" mce_href="http://11011.net/software/vspaste"&gt;&lt;/A&gt;&lt;/LI&gt;&lt;/UL&gt;
&lt;P&gt;CLR 2.0 SP2 had no fixes or changes of note in the profiling API.&lt;/P&gt;
&lt;P&gt;&lt;STRONG&gt;&lt;U&gt;Microsoft .NET Framework Versions&lt;/U&gt;&lt;/STRONG&gt;&lt;/P&gt;
&lt;P&gt;Your profiler may also (optionally) take dependencies on the libraries.&amp;nbsp; For example, if you have an instrumenting profiler that performs IL rewriting on some framework library code, then your profiler should deal gracefully with the cases that the libraries themselves may be different in the various .NET Framework versions.&amp;nbsp; Other than that, your profiler probably doesn't need to care which version of the libraries is in use.&lt;/P&gt;
&lt;H2&gt;Long Story Short&lt;/H2&gt;
&lt;P&gt;Although there are several Microsoft .NET Framework versions out there, your CLR 2.x-based profiler should still be fine.&amp;nbsp; Of course, the CLR is under active development (those libraries teams aren't the only ones having fun), so look forward to exciting things for your profiler to take advantage of in the future.&lt;/P&gt;&lt;img src="http://blogs.msdn.com/aggbug.aspx?PostID=6682435" width="1" height="1"&gt;</content><author><name>davbr</name><uri>http://blogs.msdn.com/members/davbr.aspx</uri></author></entry></feed>