Using dotTrace Perfomance API

Skip to end of metadata
Go to start of metadata

Disclaimer: This tutorial covers the usage of API only for the performance profiling method. The implementation of the timeline profiling API is very similar, with a few minor exceptions. For details on timeline profiling API classes, refer to the dotTrace documentation.

In this tutorial, we’ll learn how to use the dotTrace API in two main scenarios:

  • Profiling a specific part of the code.
  • Self-profiling of applications.
How the API Works

The dotTrace API is the main part of the dotTrace SDK. Without going into details, the API provides a number of classes which allow you to control the profiling process. For example, you can start and stop collecting profiling data, save collected snapshots and so on right from your app. 

Before going any further, let’s take a little bit more detailed look at the API usage scenarios.

Profiling a Specific Part of the Code

Typically, if your app is quite huge, you don’t need to profile it entirely. The main point of your interest is the performance of some new functionality or a certain method. Using the dotTrace API, you can narrow the profiling scope. More specifically, the API allows you to start profiling measurements and collect snapshots in the exact places of your code.

Self-Profiled Applications

dotTrace API gives you a great opportunity to collect statistics about how your app behaves on end-user desktops. Do they face any performance issues and where is the “weakest” point in your app? Due to high PC hardware fragmentation, this information may be of great interest to you.

* Must be supplied with your app. 

For this purpose, the API allows you to attach the dotTrace engine* to the running process in the background and take snapshots right from the code. Collected snapshots can be saved on the disk or passed to any other app (for example, a client that will send them to your server over HTTP). This is called self-profiling as the app, in some sense, controls the profiling by itself.

Now, let’s take some simple app and try both scenarios in action!


Sample App
Profiling a Specific Part of the Code
Self-Profiled Application


Before we continue, please accomplish the following prerequisites:

  1. Make sure you have Visual Studio 2010 or later and dotTrace 6 installed on your computer.
  2. Download and extract the archive with the sample app.
  3. Download and install dotTrace Performance SDK

Sample App

For the example, we’ll use the classic Conway’s Game of Life application. If you’re not acquainted with the app, please read the wiki article ('s_Game_of_Life). This will ease the understanding of the tutorial.
Our WPF-based implementation of Game of Life works in the following way:

  • A single instance of the Grid class is responsible for storing, updating, and displaying of a huge number of cells (instances of the Cell class).
  • The next generation of cells is created once in 200 ms: The OnTimer event handler of the DispatcherTimer instance runs the Grid.Update() method which calculates cell states for the next generation (Grid.UpdateToNextGeneration()) and updates graphics (Grid.UpdateGraphics()).

Profiling a Specific Part of the Code

Let’s assume we have made some changes to the way we calculate the next generation. Now, we want to check how the performance has changed. The dotTrace API will allow us to profile nothing but the method we want - Grid.Update(). All we need is to start collecting profiling data right before the method is executed and collect a snapshot after the execution.

  1. Open the GameOfLife solution in Visual Studio.
  2. To use the dotTrace API, you must reference the JetBrains.Profiler.Windows.Api assembly which contains the API classes. To do this:
    1. In the Solution Explorer, right click the References folder.
    2. In the context menu, select Add Reference.
    3. In the opened window, click Browse | Browse and specify the path to JetBrains.Profiler.Windows.Api.dll located in the main directory of the dotTrace SDK. 
    4. Click OK.
  3. Open the OnTimer method of the MainWindow class. As you see, it is the timer’s event handler which updates the Grid instance by running the mainGrid.Update method. This makes the OnTimer event handler a perfect candidate for injecting our dotTrace API code.

    To control the profiling process, the API uses the static PerformanceProfiler class. To start profiling, you should first call the Begin  method (creates a blank snapshot) and the Start method which actually starts measurements. Stop suspends measurements, while EndSave initiates snapshot processing in dotTrace. Let’s try out these methods in our app.

  4. For example, we need to profile only a single execution of the OnTimer event handler. To do this, we should start profiling in the beginning of OnTimer (with PerformanceProfiler.Begin and PerformanceProfiler.Start) and take a snapshot after the code is executed (with ProfilingControl.Stop and PerformanceProfiler.EndSave). After the correction, the code should look like follows:

    The genCounter field (generation counter) helps us to start and stop the profiler just once (to profile only a single mainGrid.Update() execution). The PerformanceProfiler.IsActive is used to determine whether the API is enabled. It must be checked only once.
    After the changes are made, let’s run the profiling.

  5. In Visual Studio, select the menu ReSharper | Profile | Run Startup Configuration Performance Profiling....
    This will open the profiler configuration window used to configure the profiling session.
  6. * The Tracing type is much better suited for our case. The thing is in the Sampling mode, dotTrace takes call stack information (samples) once in a period of time from 5 to 11 ms. As a single execution of the Update() method probably takes less time,  the chances it won’t be detected by the profiler are quite high. In the tracing mode, dotTrace gets information about every function entry and exit right from CLR. That’s why this method gives more detailed results.

    Change the Profiling type to Tracing*.

  7. Select Advanced to see more profiling options.
  8. Check the Use profiler API option. This will make the API commands we added to the code work.
    In the end, the profiler configuration window should look like follows.
  9. Click Run.
  10. This will run the Game of Life application and open the Controller window.

    As you can see in the Controller window, buttons used to control the profiling session (excepting Start and Kill All) are disabled. Profiling is now  controlled by the dotTrace API.
  11. As the OnTimer event handler is executed only when Game of Life is started, start the game using the Start button.
    Right after the second generation is generated, dotTrace will take a snapshot and open it in Performance Viewer.
  12. In Performance Viewer, expand the Main thread node.

    As you see, the thread view contains information only about a single call of OnTimer. Just what we needed!

Self-Profiled Application

Now, let’s move to a more complex scenario of the self-profiled application. For example, we are concerned about app performance on end-users desktops and decide to collect such statistics. Here are some considerations you should know about before implementing self-profiling:

  • End-users don’t have dotTrace installed; therefore, you must include SDK into your app’s installation package.
  • As end-users are not supposed to initiate profiling, the self-profiling session is initiated by the API.
  • For this purpose, the API uses the static SelfAttach class located in the JetBrains.Profiler.Windows.SelfApi.dll library. Calling the Attach method prepares profiling configuration and executes the profiler from the SDK folder.

    *The limitation is related with the .NET internal architecture.

  • As the profiler is attached to the already running process, there is a limitation* on the profiling type - it could be only Sampling.

Let’s try self-profiling in action.

  1. Open our Game of Life application in Visual Studio.
  2. To use the dotTrace API, you must reference the JetBrains.Profiler.Windows.SelfApi assembly which contains the API classes. To do this:
    1. In the Solution Explorer, right click the References folder.
    2. In the context menu, select Add Reference.
    3. In the opened window, click Browse | Browse and specify the path to JetBrains.Profiler.Windows.SelfApi.dll located in the main directory of the dotTrace SDK. 
    4. Click OK.
  3. First, we need to initiate self-profiling using the SelfAttach class. This can be done anywhere in the app before we call the PerformanceProfiler methods. In our case, this could be done in the constructor of the main window. To do this, open the public MainWindow() constructor in MainWindow.xaml.cs and add the following lines in the end of the constructor:

    * Some of these parameters are optional. For more information about them, refer to the dotTrace Help.

    As you see, we pass an instance of the SaveSnapshotProfilingConfig class as a parameter to the Attach method. This tells dotTrace how to process the resulting snapshot. In our case, we tell the profiler to save the snapshot file on the disk. There is also one more option you can use instead: ExecutableSnapshotProfilingConfig will run an external application passing the path to the snapshot as a command-line parameter. This option is of most interest in case you’re going to get snapshots from end-user computers remotely.
    Public fields of the SaveSnapshotProfilingConfig class allow us to specify the following profiling options*:

    • SaveDir (passed to the constructor) specifies the location where we want to save snapshot files (../ in our case).
    • ProfilingControlKind defines how the profiling must be controlled. The ProfilingControlKind.API value means we’ll control the session by means of the API. You can also decide to control the session using the Controller window or don’t control it at all.
    • RedistDir specifies the path to the dotTrace redistributables.
    • ProfilingType defines the profiling method. E.g., if you want to perform timeline profiling, you should specify ProfilingType.Timeline here.
    • ListFile stores file names of snapshots collected during profiling. This file is created automatically during profiling.
  4. As mentioned in the beginning, the only possible profiling type when using self-profiling is Sampling. Due to this, we cannot profile just a single execution of the OnTimer event handler as in the previous scenario. As the OnTimer execution probably takes less than the sampling time (5 – 11 ms), the chances it won’t be detected by the profiler are quite high. Therefore, we need to extend the profiling scope and take performance results of, for example, first 10 OnTimer executions. To do this, in the OnTimer event hanlder, we must say the profiler to stop and save snapshot only after the 10th Game of Life generation:

  5. Build and run the app.
  6. Start Game of Life using the Start button. Wait until 10 generations pass.
  7. Check the app’s folder. If everything works fine, it should contain the snapshot file.

    The resulting snapshot file can be then opened and inspected in dotTrace Performance Viewer.

Of course, the “real-life” self-profiling implementation may differ from the given example*. Thus, it would be great to handle main API exceptions, care for the cases when a user stops the game before the profiling is finished, and so on. Nevertheless, the provided example contains the minimum you should know to implement self-profiling in your app.

  • No labels