How does profiling of .NET applications work under the hood?

In a nutshell

NProfiler instructs the .NET runtime in the process to be profiled to load our profiler DLL and instantiate a class from this DLL. Our code can then respond to events in the .NET runtime, such as module loading, JIT compilation, garbage collection, etc. Our code can also create new .NET methods or inject instructions into existing methods (instrumentation). The profiler DLL then sends the collected profiling data back to the NProfiler UI via IPC or a network connection.

How is the profiler DLL loaded into a newly started process?

The .NET runtime loads a profiler DLL if specific environment variables are set.

NProfiler sets the following environment variables for profiled processes:

  • COR_ENABLE_PROFILING and CORECLR_ENABLE_PROFILING = 1: Specifies that a profiler DLL should be loaded.
  • COR_PROFILER and CORECLR_PROFILER: The COM CLSID of the profiler callback class in our profiler DLL. Specifies which DLL should be loaded and which class should be instantiated in the DLL.
  • NPROFILER_TARGET: The endpoint to which the collected profiling data should be sent (e.g., the profiler UI).
  • NPROFILER_AUTHORIZATION_TOKEN: A security token used by the profiled process to authenticate itself with NPROFILER_TARGET. This is a security measure to ensure that only profiled processes can send data to the NProfiler UI.

The COR_ environment variables are used by the old .NET Desktop runtime (up to .NET 4.*). The CORECLR_ variables are used by the new .NET Core runtime.

The environment variables are set in different ways depending on the profiled application type:

  • If an executable file is started (e.g., for the “Executable File” session type), the environment variables are set directly for the started process.
  • If a Windows service or IIS < 10 is profiled, the environment variables are set via the registry using the value HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\[Service Name]\Environment.
  • From IIS 10 onwards, the environment variables are set via the Application Pool <environmentVariables> element in the global applicationHost.config file.

How is the profiler DLL loaded into an already running process?

If you select the application type “Attach to Running .NET 4+ Process”, no environment variables can be set because the process to be profiled is already running and the .NET runtime may already have been loaded. In this case, a message is sent to the target .NET process, which causes the .NET runtime to load our profiler DLL and instantiate the profiler callback class.