I was recently asked how a managed application can know the actual size of managed objects it allocates. Although there exists a Marshal.SizeOf method that seemingly can do the trick, this only works for value types, as the method is intended to be used in interop scenarios where some unmanaged memory must be allocated in managed code and passed to some unmanaged function. This question, and others like it can best be answered by using a the .NET profiling API.
The .NET profiling API allows the (native C++) programmer to inject a DLL that can receive various events from the CLR during execution of a managed application. A .NET profiler does not only enable to “profile” an application, but also allows the code to interrogate the CLR for various pieces of information, such as object size. Interesting events might be loading of an assmbly, starting/ending of a GC cycle, creation/destruction of AppDomain, and many others.
To write such a profiler, one must use the .NET profiling API. What this means is writing a COM class that implements the ICorProfilerCallback2 interface, and meaningfully implementing the required methods for the interesting notifications. During the implementation of the Initialize method, the profiler code receives a pointer to the ICorProfilerInfo2 interface implemented by the CLR. The profiler should cache it and use in the notifications implementation to extract more info from the CLR.
Also, in the Initialize method, the code calls the CLR’s SetEventMask method to indicate which events it wants to be notified. This is an optimization, so the CLR is not bothered with calling the profiler on every event, but only on the requested events.
To install the profiler, the COM dll should be registered as all COM DLLs, using regsvr32.exe (assuming, of course the DllRegisterServer function is in there. If you use the ATL library to write the COM DLL, then this is implemented for you).
Then, 2 environment variables must be set: COR_ENABLE_PROFILING=1 and COR_PROFILER=<progid or clsid of COM class>. For example, the settings needed for tha attached sample profiler are:
The scope of setting of these variables matters. If they are set in a dedicated command prompt, all .NET apps starting from that command prompt will be profiled.
If they are set in user scope, then all .NET apps running from that user’s account are “profiled”, i.e. the DLL gets injected into the process. If the vars are set in system scope, all .NET apps are profiled.
The profiler should transmit in some way the info it gathers to some repository or other listening app that can show the info in a meaningful way.
I’ve attached a sample profiler that writes its info to a file to the c:\temp folder (so make sure this exists) as a simple log file just for demo purposes. It captures the timings of events such as start/end of GC, AppDomain creation/destruction and assembly loading. The coed is written with VS 2008 but can be written with any VS verstion. It uses ATL to create the required boilerplate COM code and just implements the necessary interface.