Using PLA.dll to manage real-time trace sessions

Spread the love

As previously discussed, PLA.dll can help you collect perf counters, collect ETW trace logs, and create performance alerts. But what about real-time trace sessions? PLA.dll can manage those, too!

A real-time trace session has some similarities to a collector set for ETW trace logs, with a few important differences:

  • The collector set name should be prefixed with the “Session” namespace.
  • The collector can only contain a single trace data collector; the collector should specify the real-time stream mode and a session name.
  • The collector set is automatically deleted when it is stopped.

Armed with this knowledge, we can update the PlaSample project with support for real-time session management.

We’ll start with the properties of the collector, defined in a new class since it is different enough from the existing trace collector type:

public class RealTimeTraceCollectorInfo
{
    public RealTimeTraceCollectorInfo(string name)
    {
        this.Name = name;
        this.Providers = new List<ProviderInfo>();
    }

    public string Name { get; private set; }

    public uint? BufferSizeInKB { get; set; }

    public TimeSpan? FlushTimer { get; set; }

    public uint? MaximumBuffers { get; set; }

    public uint? MinimumBuffers { get; set; }

    public IList<ProviderInfo> Providers { get; private set; }
}

The ProviderInfo type was already defined previously for the existing trace collector code, so we leverage it directly.

Now we just need to create the collector set. Since there are no meaningful operations that a user can do with the data collector set itself (e.g. trying to call Delete() will actually throw an exception), we’ll just return the underlying ISessionController.

public ISessionController Create()
{
    // Data collector set is the core abstraction for collecting diagnostic data.
    DataCollectorSet dcs = new DataCollectorSet();

    // Create a data collector for traces.
    ITraceDataCollector dc = (ITraceDataCollector)dcs.DataCollectors.CreateDataCollector(DataCollectorType.plaTrace);
    dc.name = this.Name;
    dcs.DataCollectors.Add(dc);

    // We need to set real-time mode and the session name
    dc.StreamMode = StreamMode.plaRealTime;
    dc.SessionName = this.Name;

    // Set various values (if present)
    SetValue(dc, this.BufferSizeInKB, (d, v) => d.BufferSize = v);
    SetValue(dc, this.FlushTimer, (d, v) => d.FlushTimer = (uint)v.TotalSeconds);
    SetValue(dc, this.MaximumBuffers, (d, v) => d.MaximumBuffers = v);
    SetValue(dc, this.MinimumBuffers, (d, v) => d.MinimumBuffers = v);

    // Build up the list of providers.
    foreach (ProviderInfo providerInfo in this.Providers)
    {
        TraceDataProvider provider = dc.TraceDataProviders.CreateTraceDataProvider();
        dc.TraceDataProviders.Add(provider);

        provider.Guid = providerInfo.Id;
        AddValue(provider.KeywordsAll, providerInfo.KeywordsAll);
        AddValue(provider.KeywordsAny, providerInfo.KeywordsAny);
        AddValue(provider.Level, providerInfo.Level);
    }

    // Now actually create (or modify existing) the set.
    // We explicitly specify the 'Session' namespace for real-time collectors.
    dcs.Commit("Session\\" + this.Name, null, CommitMode.plaCreateOrModify);

    // Return an opaque wrapper with which the user can control the session.
    return new CollectorSetWrapper(dcs);
}

private static void SetValue<TClass, TValue>(TClass c, TValue? v, Action<TClass, TValue> setValue) where TValue : struct
{
    if (v.HasValue)
    {
        setValue(c, v.Value);
    }
}

private static void AddValue<TValue>(IValueMap map, TValue? v) where TValue : struct
{
    if (v.HasValue)
    {
        map.Add(v.Value);
    }
}

The user just has to call Start() to begin delivering events and Stop() to stop and delete the session, as shown in this sample code:

private static void CreateRealTimeTraceCollector()
{
    RealTimeTraceCollectorInfo info = new RealTimeTraceCollectorInfo("MyRealTimeCollector");

    // Microsoft-Windows-Kernel-Process         
    Guid providerId = new Guid("{22FB2CD6-0E7B-422B-A0C7-2FAD1FD0E716}");

    info.Providers.Add(new ProviderInfo(providerId) { Level = 5 });

    ISessionController controller = info.Create();
    controller.Start();

    Thread.Sleep(5000);

    controller.Stop();
}

As always, remember to run elevated to manage real-time trace sessions.

Leave a Reply

Your email address will not be published. Required fields are marked *