As you know if you are a Visual Studio Team System user, we provide two types of profilers with the product; sampling and trace. If you are not familiar with this feature of VSTS, one place to start are the excellent TechNotes under the Development heading on this page.
The trace profiler works by rewriting the method bodies of your program to include probes. A probe is a small piece of code, usually a function call, that calls out to the profilers collection logic so that it can record the current location of the program execution.
The subject of this blog is how those probes get into your methods. This process is called instrumentation. I’ll discuss two approaches, and then I’ll discuss the pros and cons of each.
In this approach the instrumentation occurs before your program even runs. Essentially the probes are baked in to you binary. There are two ways this can be done. One is to have the compiler put the probes in. Visual C++ takes this approach to implent Profile Guided Optimizations (PGO). Another is to do what is called post link instrumentation. With this approach you take the binary that has been emitted by the compiler, crack it open to find all the methods, instrument and then rewrite the binary like nothing ever happened.
Pros & Cons:
This approach yields much faster start-up times. The images that is loaded in to memory is the image that runs.
The rewrite process destroys .NET strong naming, and thus images must be resigned after instrumentation, or verification skipping must be enabled. Statically instrumented images can also be placed in the native image cache.
Rewriting the images means you have to deal with the whole question of where to put the files that you instrument. This can be quite a housekeeping chore for the user.
Rewriting PE files correctly is relatively straightforward for .NET applications. It’s really hard for native applications, particularly for x64 images, where instrumenting methods can turn them from leaf methods to non-leaf methods.
In this approach the instrumentation occurs as the program is running. In the case of a just in time compilation environment such as .NET, this is acheived by using the .NET profiling API to get notified when methods are about to be compiled and executed for the first time. Your profiler gets the opportunity to rewrite the method that is about to be run, by adding probes or whatever you want. Then the program continues on. Dynamic instrumentation can also be performed on native images too, but doing this is hard and rarely done. The approaches you could take to pull this involve injecting a DLL into the process, suspending it and then using debug information to patch and rewrite methods.
Pros & Cons
Yields significantly slower start-up times and increased memory usage because each method body must be reallocated, as well as the the some CPU load to instrument on the fly.
If you want to do instrumentation at the line level, this approach requires loading additional data from the .pdb files in at runtime too. If you load the actual .pdb’s that has a huge runtime cost.
Because the original file was not modified, .NET strong naming is not broken. However, profiling of images in the native image cache must be disabled, as these cannot be dynamically instrumented because they are already compiled to native code
Hopefully this entry has given you an overview of the two approaches. Please comment with any question you may have.