GLAD is available


End of last year I mentioned we wanted to provide an API for you to really investigate GC/managed memory related performance called GLAD. Well, the source finally got opened source on github. So GLAD is available. The repo is called PerfView but you actually just need the TraceEvent project (but it’s much easier to just build the whole solution then add the reference to the resulting Microsoft.Diagnostics.Tracing.TraceEvent.dll). Below is a very simple example of getting the total GC pause time for each process (that has GC pauses) and printing out this info along with the process name and pid.

using System;
using System.Diagnostics.Tracing;
using Microsoft.Diagnostics.Tracing.Session;
using Microsoft.Diagnostics.Tracing.Parsers;
using Microsoft.Diagnostics.Tracing.Analysis;
using Microsoft.Diagnostics.Tracing.Analysis.JIT;
using Microsoft.Diagnostics.Tracing.Analysis.GC;
using Microsoft.Diagnostics.Tracing.Parsers.Clr;
using System.Collections.Generic;

namespace GCInfoProcessing
{
    class Program
    {
        // Given an .etl file, print out GC stats.
        static void DecodeEtl(string strName)
        {
            using (var source = new Microsoft.Diagnostics.Tracing.ETWTraceEventSource(strName))
            {
                Console.WriteLine("{0}", strName);
                source.NeedLoadedDotNetRuntimes();

                source.Process();
                List GCs = null;

                foreach (var proc in source.Processes())
                {
                    var mang = proc.LoadedDotNetRuntime();
                    if (mang == null) continue;

                    int total_gcs = 0;
                    double total_pause_ms = 0;

                    // This is the list of GCs with processed info
                    GCs = mang.GC.GCs;
                    for (int i = 0; i < GCs.Count; i++) 
                    { 
                        TraceGC gc = GCs[i]; 
                        total_gcs++; 
                        total_pause_ms += gc.PauseDurationMSec; 
                    } 
                    if (total_gcs > 0)
                         Console.WriteLine("process {0} ({1}): total {2} GCs, pause {3:n3}ms", 
                                           proc.Name, proc.ProcessID, total_gcs, total_pause_ms);
                }
            }
        }

        static void Main(string[] args)
        {
            DecodeEtl(args[0]);
        }
    }
}

I’ll give a brief description of how things work for GLAD but with the code publicly available it should be fairly easy to just build and step through the code to see how it works.

TraceEvent\Computers\TraceManagedProcess.cs processes the GC ETW events and generates the info available in the TraceGC class (I edited the comments so they don't cause trouble for html):

public class TraceGC
{
    // Primary GC information
    // Set in GCStart (starts at 1, unique for process)
    public int Number;
    // Type of the GC, eg. NonConcurrent, Background or Foreground
    // Set in GCStart
    public GCType Type;
    /// Reason for the GC, eg. exhausted small heap, etc.
    // Set in GCStart
    public GCReason Reason;
    /// Generation of the heap collected. If you compare Generation at the start and stop GC events they may differ.
    // Set in GCStop(Generation 0, 1 or 2)
    public int Generation;
    /// Time relative to the start of the trace. Useful for ordering
    // Set in Start, does not include suspension.
    public double StartRelativeMSec;
    /// Duration of the GC, excluding the suspension time
    // Set in Stop This is JUST the GC time (not including suspension) That is Stop-Start.
    public double DurationMSec;
    /// Duration the EE suspended the process
    // Total time EE is suspended (can be less than GC time for background)
    public double PauseDurationMSec;
    //......
}

You will see a bunch of fields with this comment:

[Obsolete("This is experimental, you should not use it yet for non-experimental purposes.")]

It’s not obsolete – it’s just experimental. We wanted to organize the info available in TraceGC in a user friendly way (and you are welcome to suggest/contribute to it!) and we want your input to really flesh out the API aspect. Do we just want to expose these as is, or want to have a more advanced class to represent info that’s less frequently used? It’d be great to hear some opinions on this. Feel free to either leave them as comments here or on the github repo.
The process part is done at the beginning of this file. An example is

source.Clr.GCStart += delegate (GCStartTraceData data)
{
    // ....
};

If you want to see examples of how TraceGC is used, you can get plenty of such examples in PerfView\GcStats.cs – this is what generates the GCStats view in PerfView.

Looking forward to seeing the analysis that folks write on memory analysis for .NET 🙂


Comments (8)

  1. Thanks for all these details and the great work on TraceEvent Maoni!
    I’m trying to get information on the GC behaviour, not from an .etl file but for other running processes in a live session.
    The documentation of the GCSuspendEE_V1 event (https://msdn.microsoft.com/en-us/library/ff356162.aspx) states that the Count property should contain the number of suspended threads… but TraceEvent (via the GCSuspendedEETraceData class) seems to return the collections count instead.
    The ClrTraceEventParser.cs implementation looks for GCSuspendEEReason as a Int32 but the MSDN page mentions an UInt16 and the Count is searched at offset 4 (which might be 2 if the reason is really 16 bit long). Do you know who could look at this? :^)

    1. Hi Christophe, there were 2 versions of the GCSuspendEE event, version 0 has the Reason field as UInt16 but GCSuspendEE_V1 has it as UInt32. The doc for Count on MSDN is definitely wrong, I will ask our UE folks to correct this. Thanks for pointing it out! This field is the GC count *at the time*. And since the GC count only gets increased during a GC, the GCStart event that you usually would see following the SuspendEE events would have a Count that’s 1 higher.

      1. Happy to “help”
        Thanks Maoni

  2. [[Matt]] says:

    Is it possible to get an analysis of how the GC differs on .Net core from .Net Framework?
    thanks

    1. Yes. It’s almost exactly the same – it uses the same GC source 🙂 Note that of course the OS support could be different – on Linux we have different OS support. This part of the source is in pal in the coreclr repo.

  3. onurg says:

    A quick question, not related to GLAD. Starting with .NET 4.5 you’ve put an option to compact LOH by using
    GCSettings.LargeObjectHeapCompactionMode. My question is suppose that we never used this option, does GC ever compact LOH by itself I mean even such as extreme conditions like Windows is low on memory. Does GC try to compact LOH by itself for such situations ?

    1. Not currently but I am thinking about enabling it in such extreme situations.

Skip to main content