About the Profiling API
A profiler is a tool that monitors the execution of another application. A common language runtime (CLR) profiler is a dynamic link library (DLL) that consists of functions that receive messages from, and send messages to, the CLR by using the profiling API. The profiler DLL is loaded by the CLR at run time.
Traditional profiling tools focus on measuring the execution of the application. That is, they measure the time that is spent in each function or the memory usage of the application over time. The profiling API targets a broader class of diagnostic tools such as code-coverage utilities and even advanced debugging aids. These uses are all diagnostic in nature. The profiling API not only measures but also monitors the execution of an application. For this reason, the profiling API should never be used by the application itself, and the application’s execution should not depend on (or be affected by) the profiler.
Profiling a CLR application requires more support than profiling conventionally compiled machine code. This is because the CLR introduces concepts such as application domains, garbage collection, managed exception handling, just-in-time (JIT) compilation of code (converting Microsoft intermediate language, or MSIL, code into native machine code), and similar features. Conventional profiling mechanisms cannot identify or provide useful information about these features. The profiling API provides this missing information efficiently, with minimal effect on the performance of the CLR and the profiled application.
JIT compilation at run time provides good opportunities for profiling. The profiling API enables a profiler to change the in-memory MSIL code stream for a routine before it is JIT-compiled. In this manner, the profiler can dynamically add instrumentation code to particular routines that need deeper investigation. Although this approach is possible in conventional scenarios, it is much easier to implement for the CLR by using the profiling API.
Typically, the profiling API is used to write a code profiler, which is a program that monitors the execution of a managed application.
The profiling API is used by a profiler DLL, which is loaded into the same process as the application that is being profiled. The profiler DLL implements a callback interface (ICorProfilerCallback in the .NET Framework version 1.0 and 1.1, ICorProfilerCallback2 in version 2.0). The CLR calls the methods in that interface to notify the profiler of events in the profiled process. The profiler can call back into the runtime by using the methods in the ICorProfilerInfo and ICorProfilerInfo2 interfaces to obtain information about the state of the profiled application.
Only the data-gathering part of the profiler solution should be running in the same process as the profiled application. All user interface and data analysis should be performed in a separate process.
The following illustration shows how the profiler DLL interacts with the application that is being profiled and the CLR.
ICorProfilerCallback and ICorProfilerCallback2 can be considered notification interfaces. These interfaces consist of methods such as ClassLoadStarted, ClassLoadFinished, and JITCompilationStarted. Each time the CLR loads or unloads a class, compiles a function, and so on, it calls the corresponding method in the profiler's ICorProfilerCallback or ICorProfilerCallback2 interface.
For example, a profiler could measure code performance through two notification functions: FunctionEnter2 and FunctionLeave2. It just time-stamps each notification, accumulates results, and outputs a list that indicates which functions consumed the most CPU or wall-clock time during the execution of the application.
The other main interfaces involved in profiling are ICorProfilerInfo and ICorProfilerInfo2. The profiler calls these interfaces as required to obtain more information to help its analysis. For example, whenever the CLR calls the FunctionEnter2 function, it supplies a function identifier. The profiler can get more information about that function by calling the ICorProfilerInfo2::GetFunctionInfo2 method to discover the function's parent class, its name, and so on.