mmaitre314 / TraceEtw

High-performance tracing for C++ Desktop and Windows/Windows Phone Store apps
http://mmaitre314.github.io/TraceEtw
Apache License 2.0
14 stars 3 forks source link

Build statusTest status NuGet package

Event Tracing for Windows (ETW) is powerful but notoriously complex. In C#, EventSource made that technology much more approachable. This project aims at providing a similar solution for C++, both for Desktop apps and for Windows/Windows Phone Universal Store apps.

Defining events

To begin with, add an XML file with an .epx extension (as in 'Event Provider XML') to the Visual Studio project:

<?xml version="1.0" encoding="utf-8"?>
<EventProvider
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="http://mmaitre314.github.io/EventProvider.xsd"
    Name="MMaitre-TraceEtw" 
    >

</EventProvider>

where the Name attribute should be replaced with some appropriate value.

A basic marker event with no payload can be added using:

<Event Name="Marker" />

When the project is compiled a header gets generated from the XML file, which lets the app raise the marker event when appropriate:

#include "Events\EtwLogger.h"

EtwLogger.Marker();

where EtwLogger is the name of the .epx file.

Windows Performance Analyzer (WPA) will start displaying the event:

WPA

The XML file allows defining more complex events. Events can have arguments and a trace level:

<Event Name="Trace" Level="Verbose">
    <Arg Type="Pointer" Name="Object" />
    <Arg Type="UInt32" Name="Count" />
</Event>
EtwLogger.Trace(this, 314);

The default trace level is Informational.

Events can also have a variable number of arguments, which generates unstructured traces (i.e. printf):

<Event Name="Trace">
    <VarArgs Type="AnsiString" Name="Message" />
</Event>
EtwLogger.Trace("Received %i calls from %s", 3, "localhost");

Defining task events allows tracking the beginning and end of long operations:

<Task Name="ALongOperation">
    <Start>
        <Arg Type="Pointer" Name="Object" />
    </Start>
    <Stop>
        <Arg Type="Pointer" Name="Object" />
        <Arg Type="Int32" Name="HResult" />
    </Stop>
</Task>
EtwLogger.ALongOperationStart(this);
...
EtwLogger.ALongOperationStop(this, S_OK);

The IsEnabled() method on the logger class allows traces which may require expensive computations to only run when events are being recorded:


if (EtwLogger.IsEnabled())
{
    string message = GatherTraceInformation()
    EtwLogger.Trace(L"Trace information: %s", message.c_str());
}

Defining channels

Windows Event Viewer can be extended with your own log under the Applications and Services Logs. These logs are called channels. A channel is a sink that collects events.

Define your own log using the Channel element. You must associate each event with a channel, otherwise it won't be collected.

<Channel Id="ch1" Name="ChannelSymbolicName" Type="Admin" Enabled="true" Message="Display name of the log" />

<Event Name="Marker" Channel="ch1" Message="Message displayed in Event Viewer logs." />

Recording and displaying events

Besides the logger header, the build also generates a set of scripts, a WPRP profile, and an event-provider manifest:

The files are placed in the output folder along with the binaries. Registering the manifest is optional when using WPR.

The trace script relies on xperf to record events. It is part of the Windows Performance Toolkit like WPA and WPR. Its ability to run a merge pass on the .etl event log files tends to make it more robust when it comes to collecting event-manifest info.

In-app event recording

The project also contains an API for apps to record events fired inside their own process. This is currently not included in the NuGet package as it works in Windows apps but not in Windows Store apps (EnableTrace() function banned there).

Recording events is just a matter of creating an InProcEventListener object, passing a file path and the list of event provider GUIDs to enable:

auto listener = ref new InProcEventListener(
    ApplicationData::Current->LocalFolder,
    L"log.etl",
    ref new Vector<Guid> { MMaitre_TraceEtw }
);

EtwLogger.Trace("1");