Focus on Microsoft Technologies - Tutorials, Articles, Code Samples.

Thursday, August 31, 2006

.NET Profiling API

  The Microsoft® .NET Common Language Runtime (CLR) does many things under the hood to create an easy to use, object-oriented platform. The list includes garbage collection, standardized cross-language exception handling, a comprehensive class library, metadata, interoperability with existing native code, and remoting. It also includes a cross-CPU instruction format (Intermediate Language, or IL), and a JIT compiler to turn the IL into code executable by the target CPU.
As systems evolve and grow more complex, the ability to peer into their inner workings becomes increasingly valuable. In Windows®, seeing into the operations of mechanisms such as the executable loader or the memory manager revealed a hodgepodge of various techniques without much consistency between them. In addition, something that worked on the Windows 9x platform may not have worked on Windows NT® and Windows 2000, or vice versa. The most evolved mechanism for seeing the inner workings of a process under Win32® was the debug API, and even that consisted of only eight notifications.
With .NET, the story is radically different. Because the CLR runtime is so central to any .NET program, it provides a logical place to insert hooks to see the guts of .NET. Anticipating the needs of tools developers and system-level programmers, Microsoft used the clean slate of .NET to create a very detailed and consistent way to observe the inner workings of a process (and in some cases, change the way it operates). This month, I'll describe this mechanism, as well as present a program (DNProfiler) for logging the operations of the .NET runtime.

The .NET Profiling API
One means by which you can observe the .NET runtime in action is by using the profiling API. The profiling API is badly named, since it's useful for all sorts of things beyond just profiling. Consider the list of .NET actions that can be seen via the profiling API, shown in Figure 1.
As Keanu Reeves might say, Whoooa! After years of figuring out how to hook into Windows the hard way, Microsoft has gone and made it downright easy (relatively speaking) to observe the CLR as it goes about its business. Every action from the list is seen as a distinct callback. In theory, just write the desired code in the appropriate callback and you're good to go.
Currently, the best documentation for using the profiling API is the Profiling.doc file in the .NET SDK, in the Tool Developers Guide\docs directory. For the Beta 2 time frame, this file is slightly out of sync with the actual implementation. In this column, I won't exhaustively cover every aspect of the profiling API, but will instead concentrate on the big picture of what it can do.

Profiling API Contains the following concepts.

1. .NET Profiling API
2. .NET Profiling API Interfaces
3. Initialize/Shutdown Methods

Initialize is the first method called when a .NET process uses the profiling API. Your code gets its ICorProfilerInfo pointer from this method. The single parameter to Initialize is an LPUNKNOWN, on which you call QueryInterface to get the ICorProfilerInfo pointer. In addition, Initialize is where you tell the profiling API which events you're interested in.
To indicate interest in events call ICorProfilerInfo::SetEventMask, passing in a DWORD with the appropriate bits set. The flag values are from the COR_PROF_MONITOR enum in CorProf.H. The lower value flags are named COR_PRF_MONITOR_XXX, and tell the CLR which ICorProfilerCallback methods to invoke. For example, if you want the ClassLoadStarted method to fire, you must set the COR_PRF_MONITOR_CLASS_LOADS flag.
The remaining flags to ICorProfilerInfo::SetEventMask alter the behavior of the CLR in one way or another. For example, the COR_PRF_ENABLE_OBJECT_ALLOCATED flag must be set if you want to monitor object allocations at any time during execution. Similarly, the COR_PRF_DISABLE_INLINING tells the CLR not to inline any methods. If a method is inlined, you won't receive method enter and leave callbacks for it.
You can modify which events are monitored later on by calling ICorProfilerInfo::SetEventMask again. However, certain events are immutable, meaning that once they're set in Initialize, they can't be changed later.
The Shutdown method fires when the CLR is shutting down the process. In certain cases it may not fire, but for a normal .NET program with a normal lifetime, it should fire.
4. Application Domain Creation/Shutdown
5. Assembly Loads/Unloads
6. Module Loads/Unloads
7. Class Loads/Unloads
8. JIT Compilation
9. Threading
10. COM Interop
When the CLR interfaces with classic COM, it does so via proxy interfaces. These two methods—COMClassicVTableCreated and COMClassicVTableDestroyed—indicate when the proxies are created and destroyed. The information conveyed for the creation method includes the .NET ClassID, the corresponding COM interface IID, a pointer to the proxy's vtable, and the number of vtable entries.
11. Managed/Unmanaged Code Transitions
UnmanagedToManagedTransition and ManagedToUnmanagedTransition are called when managed code calls into unmanaged native code, and vice versa. Both methods are passed a FunctionID representing the callee, which can be passed to ICorProfilerInfo::GetFunctionInfo for more information.
12. Garbage Collection and Managed Heap Objects
13. Remoting Activity
14. Exception Handling
The profiling API's support for exception handling is rather complex and quite complete. I'll defer to the documentation for all the details. In a nutshell, however, the API gives you before and after notifications for each phase of the exception handling sequence. Figure 5 shows the exception methods.
The ExceptionThrown method is the first indication you receive of an exception. The ObjectID parameter can be passed to m_pICorProfilerInfo::GetClassFromObject to get the type of the exception. For every managed method on the stack, these methods tell you when the CLR's exception code searches for a try block in the method, executes a filter function in the method, or finds a method to handle the exception. It also tells you when the exception code unwinds a method, executes a finally block while unwinding, or executes the handler code.
15. Receiving Method Entry and Exit Notifications
16. Caveats with the Profiling API

Continue for the details of each point from microsoft.

, , , , ,

Post a Comment