Page Comparison - Getting Started with Timeline Profiling (v.36 vs v.43)

Versions Compared


  • This line was added.
  • This line was removed.
  • Formatting was changed.

What Is Timeline Profiling?

Since the version 6.0, dotTrace offers a new method of profiling your apps called timeline profiling. What’s that What is timeline profiling and why do you need it?

Unlike “classic” 'classic' performance profiling in dotTrace 5.5 and earlier, during timeline profiling , dotTrace collects temporal call stack and thread state data. Thus, you You get the same data about call times, but now these data is bound to the a timeline. This gives you a great opportunity to way, you can analyze not only typical “what is the slowest method?” issues, but also the ones where the order of events does matter: , such as UI freezes, excessive garbage collections, uneven workload distribution, insufficient file I/O, and othersso on.
Using To start timeline profiling is simple: all you need is to , simply choose the Timeline profiling type when configuring a session. To analyze collected timeline profiling snapshots, you should use a separate dotTrace component called Timeline Viewer.

What Next?

In this “Getting Started” Getting Started tutorial, we will take a detailed look at the main key profiling steps , acquaint with and introduce you to the Timeline Viewer user interface and even try to solve . We will also profile a sample app and try to determine why its UI freezes, which is a very common task – finding a reason of UI freezes in an appprofiling task.


Sample App
 Running the Profiler and Getting a Snapshot
First Look at the Timeline’s Timeline User Interface
 Analyzing a Snapshot in Timeline

Sample App
Sample App
Sample App


As an example, we’ll use a small app Our sample app is used to reverse lines (in text files, e.g., "ABC" > "CBA") in text files. Briefly: With the Select Files button, a user specifies chooses one or more text files to be processed. The Process Files button runs a separate BackgroundWorker thread which reverses lines in the files. The progress of file processing Progress is displayed in a label on the main the left-hand corner of the window. After the processing is finished, the label shows the "says All files were successfully processed" message.
The source code of the for this app is available here.


*The algorithm of fixing UI freezes shown in this tutorial contains some unnecessary steps and may seem suboptimal to you suboptimal. This is made done intentionally as the main goal of the tutorial is to fully acquaint you with Timeline as maximally as possibleViewer.

The app has a serious drawback. After starting file processing, users experience long UI lags that last until the processing is over.
Let’s use timeline profiling to determine the cause of find out why these freezes happen!*

Running the Profiler and Getting a Snapshot
Step 1
Step 1


  1. Open the MassFileProcessing.sln solution in Visual Studio.
  2. In case If you have dotTrace integrated with Visual Studio, run the profiler by choosing ReSharper | Profile | Profile Run Startup Project (Configuration Performance )Profiling.
    Otherwise, run dotTrace from the Windows Start menu. In dotTrace, select New Session | Local, Profile Application | Standalone.
  3. In Profiling type, select Timeline. In case you ran dotTrace as a standalone app, you should also specify the path to the release executable of our sample app in Application.
  4. After you click Run, dotTrace runs our app and displays a special controller window used to control the profiling process.

    Now, we should let's try to reproduce a performance issue in our app.
  5. Click the Select Files button and choose five text files that come with the app in the Text Files folder.
  6. Click the Process Files button to start file processing.
    As you can see, the app lags very badly. Actually, you are even unable to see the progress of file processing until it is finished and the All files were processed successfully message is shown.
  7. Collect a timeline profiling snapshot by clicking Get Snapshot’n’Wait in the controller window. The snapshot will be opened in Timeline Viewer.
  8. Close the app. This will also close the controller window.

First Look at


Timeline User Interface
Step 2
Step 2


Now, let’s Let’s make a little digression and take a look at the Timeline UI.


So, where are the filters? Almost all windows you see inside While most windows in Timeline Viewer not only display data but , they are also are used to set a specific filterfilters.* . The result of every filter’s work is always a set of time intervals or point events selected by a specific condition. For example, clicking the Interval Filters | File I/O in the Filters window will Select all time intervals where threads performed file I/O operations. Clicking on the Main thread in the Threads diagram will Select lifetime of the Main thread.

Of course, filters can be chained together. Thus, if you turn on If you activate the two filters mentioned above, you will get the resulting filter , Select all time intervals where the Main thread performed file I/O operations”. Complex filter combinations allow you to . By carefully combining filters, you can investigate almost every aspect of your application.

Actually, that's all you This is all we need to know before starting your work in to working with Timeline Viewer. Now, it's time Time to try it in action!

Analyzing a Snapshot in Timeline
Step 3
Step 3


  1. Note

    *You can make them visible at any time.

    Look at the Threads diagram. By default, it contains all application threads excluding






    Note that all filter values you see are calculated for all threads currently visible



    From the point of

    For further analysis, we are not interested in


    threads that

    do not



    no work. So, first


    let’s get rid of




    Image Modified

  2. Look at the list of threads on the diagram. It consists of the Main app thread, the Finalizer thread (is used to finalize objects but does not do any no work in our app), and Garbage Collection thread (is used to perform background GC). The Background thread that processes files in our app was identified as Thread Pool (ID 10104) because background threads are created by the CLR Thread Pool. There's also one more Thread Pool (ID 2900) that doesn't do any does no work. Probably, this This is probably some auxiliary CLR thread pool. 
    Let's hide the Finalizer and Thread Pool (ID 2900) threads as meaningless for irrelevant to our analysis.
  3. Select the Finalizer and Thread Pool (ID 2900) threads in the Threads diagram.
  4. Right-click and in the context menu select Hide Selected Threads.
  5. Look at the Threads diagram and the status bar. The following filter that is now currently applied to the snapshot data is “: Select lifetime intervals of all threads excepting hidden.

    Note how the data in other filters were affected. For example, state times in Thread States are now calculated for all threads excepting except the hidden ones. Top Methods and Call Tree have changed too, showing calls only from the filtered threads. 
  6. The current scale of the Threads diagram doesn't allow us to see the 10104 Thread Pool (our BackgroundWorker thread) in details. Let’s zoom in so that it fits the entire diagram.
    To do this, use Ctrl+Mouse Wheel on the Threads Diagram.
    This automatically adds the the filter Visible time range: 1586 ms filter. Note how this filter affects others: all values are recalculated for the visible time range.
    The following filter that is now applied to the snapshot data is “: Select all time intervals within the visible time range for all threads excepting hidden.
  7. Take a look at the Threads diagram.
    What you see is how thread states changed over time. For example, our BackgroundWorker thread 10104 Thread Pool was started approximately on at 16.3 s (after we clicked the Process Files button). Most of the time the thread was Running (rich blue intervals). Besides, there are intervals where the thread was in the Waiting state (pale blue intervals).
  8. Note

    *The timeline pattern may look slightly different on your computer, as the execution flow depends on huge a great variety of factors (like a the number of CPU cores, their workload during the app execution, and so onetc.). Nevertheless, the freeze must take place regardless of your system configuration.

    Look at the Process Overview diagram.*


    In addition to CPU Utilization, it shows two event diagrams


    relevant for our performance analysis

    : the

    . The UI Freeze bar shows that the freeze started right after


    10104 Thread Pool was created. Blocking Garbage Collection was also intensively performed on this time interval. As blocking GC suspends all managed threads, it may be the potential cause of the UI freeze.
    Image Modified

    So, it’s better to

    We must take a closer look


    at these events.

  9. First, let’s remove the Visible time range filter as we no longer need it. To do this, click on the filter in the list of applied filters. This The diagram will zoom the diagram back out.
  10. Now, let’s investigate the UI freeze event more thoroughly.
    What are the main reasons potential causes of such freezes? These are:
    • Long long or frequent blocking GCs.;
    • Blocking blocking of the UI thread by some other thread (for example, due to lock contention).Excessive ; and/or
    • excessive computational work on the UI thread.
      Therefore, all we need to do is exclude all reasons We will proceed to exclude these causes one by one until we find the real oneonly the true culprit remains.
  11. Select the UI freeze event by clicking on the corresponding bar in the Process Overview section. This will apply the filter by the UI freeze event. Note that this applies not only the filter by the freeze time range, but also the filter by the Main thread. The latter is done automatically, as the Main thread is the only one that processes the UI in our app.
    Thus, the resulting filter now is Select all time intervals on the Main thread where the UI freeze event took place.
  12. Now, we should understand what the real reason to identify the true cause of this freeze was. Let’s , let’s investigate values in the Filters window.
    The first reason potential cause we should analyze is excessive blocking GCs. Take a look at the Blocking GC filter. Taking into account the currently applied filters, it shows how long the Main thread was (Blocking GC value) and was not (Exclude Blocking GC value) blocked by GC during the freeze.

    The Blocking GC time is quite high (483 ms, or 11.8% of the selected interval) and probably may have some impact on performance. Nevertheless, it could hardly be the reason cause of the 4 s long -second freeze. Thus, as for now, the excessive GC reason can be excludedwe may exclude excessive GC from our list of suspects.
  13. Click the Exclude Blocking GC value. The resulting filter now is Select all time intervals on the Main thread where the UI freeze event took place and no blocking GC is performed.
  14. Let's investigate the potential causes “Blocking by some other thread” and “Excessive work on the Main thread” reasonsthread.
    Look at Thread State. This filter shows total time spent by threads in a certain state. Taking into account the currently applied filters, it shows states of the Main thread during the freeze.

    It appears that most of the freeze time (92.1%, or 3335 ms), the thread was doing some work as it was Running. The 242 ms value of the Waiting state is too small, which automatically excludes the “Blocking by some other thread” reasonas a cause. Therefore, the cause of the freeze is a can only be computational work on the Main thread!
    All we need to do now is to find methods that were executed on the Main thread during the freeze. For this purpose we We can use the Top Methods and Call Tree filters for this.
  15. Select Running in the Thread States filter. This will make the resulting filter Select all time intervals where the Main thread was running when the UI freeze took place and no blocking GC was performed.
    The list of filters will look like as follows:

    Now,  the filters Top Methods and Call Tree filters contain only methods executed during these time intervals.
  16. Look at the Top Methods filter. It shows the plain list of methods from the stack sorted by their execution time.

    In the current state, the list doesn't make a lot of sense as it is filled mostly with the low-level system methods. Let's make the list more meaningful.
  17. Turn on Select the Hide system methods checkbox check-box. In this mode, Top Methods shows only user methods. The execution time of a user methods method is calculated as a the sum of the method's own user method time and the own time of all system methods it calls (down to the next user method in the stack).

    There We see there are only two methods left: App.Main and ProcessInProgress.
  18. Look at the Call Tree.
    Image Modified


    * By default, to simplify To ease understanding of the call tree, Timeline  Timeline Viewer folds system call chains by default. To unfold the chain, click the  icon icon shown against the root system call.

    As you can see, App.Main spends most of the time in a number of system methods related to processing Windows messages.*


    This is


    typical behavior for any app with

    the visual

    a graphical UI. This indicates that the app waits for user input in a message loop. We can simply ignore these methods when analyzing the snapshot. To find out what method causes the freeze, we should look at the next user method in the stack, which


    turns out to be ProcessInProgress (955 ms, or 28.6%).

    We can make an assumption

    Since we assumed that lags took place due to some computational work in this method

    . Let’s

    , let’s check its source code.

  19. In Call Tree, click on the ProcessInProgress method.
  20. Note

    *If the Source View window is not opened by default, open it using the View | Source View menu.

    Look at Source View window.*


  21. Code Block
    private void ProcessInProgress(object sender, ProgressChangedEventArgs e)
       var upd = (ProgressUpdater) e.UserState;
       lblProgress.Content = "File " + upd.CurrentFileNmb + " of" + upd.TotalFiles + ": " + e.ProgressPercentage + "%";

    It appears that this method is just an event handler that updates the progress of file processing operation in


    the progress label on the main window.

    Definitely, this doesn

    These computations don't look




    , so why did the freezes occur?

    The answer is simple.

    Apparently, this event handler is called so often that the main window


    cannot cope with updating


    the label. Let’s check this out in our code.

  22. Note

    *If you use JetBrains ReSharper, you can employ a the very helpful Inspect This feature (press Ctrl+Shift+Alt+A combination). It is able to can show you all incoming calls to the method.

    Switch to Visual Studio and look at the code.
    Further code investigation* shows


    that this event handler is subscribed


    to the ProgressChanged event of the background worker. This event occurs when the worker calls the ReportProgress method. In turn, it is called from the ProcessFiles method of the background worker.

    Code Block
    for (int j = 0; j < _lines.Length; j++)
       var line = _lines[j];
       var stringReverser = new StringReverser(line);
       _lines[j] = stringReverser.Reverse();
       if (j % 5 == 0)
          var p = ((float)(j + 1) / _lines.Length) * 100;
          Worker.ReportProgress((int) p, _updater);
  23. Here is the cause of our performance issues

    : ReportProgress is called each time after 5 lines in a text file are processed. As lines are processed very quickly, ReportProgress is called too frequently for the system. Let’s reduce this frequency, for instance, to one call

    for a

    per 1000 lines. Improve the if condition in the code.

    Code Block
    if (j % 1000 == 0)
       float _p = ((float)(j + 1) / _lines.Length) * 100;
       Worker.ReportProgress((int) _p, _updater);
  24. Rebuild the solution and perform profiling one more time as described in Running the Profiler and Getting a Snapshot.
  25. Here There it is! No more lags. Timeline also doesn’t detect any UI freezes during file processing.



Here is a short summary of What we learned in this Getting Started tutorial:

  • Unlike “classic” performance profiling, during timeline profiling dotTrace collects temporal call stack and thread state data.
  • To analyze the results of timeline profiling, a special Timeline Viewer is used.
  • Timeline Viewer is a set of filters and diagrams that visualize the event timeline of your app and allow you to slice and dice the collected temporal data.
  • Each filter not only sets is dual-purpose: it both displays data and lets you set a specific condition but also displays the data.
  • Filters can be chained together.

In next the following tutorials, we will learn how to use Timeline to improve app performance in more complex scenarios. For , for example , when excessive garbage collections or file I/O operations take place, or when you face some multithreading issue like lock contention.