.NET Event Tracing for Windows

I didn’t find a good step-by-step guide on how to create a .NET Event Tracing for Windows (ETW) provider with a manifest. And there are some caveats I would like to share with the community so here I go.

The very first thing I recommend is to familiarize with ETW concepts like the manifest, session, provider, events, channels, etc. And the second thing is to install the latest version of the Microsoft Windows SDK (v6.1 as I am writing this) because several utilities ease working with ETW. Those tools are the Manifest Generator (ecmangen.exe), the Windows Message Compiler (mc.exe) and the Resource Compiler (rc.exe).

Now you need to create the XML 1.0 document known as the ETW manifest. Although you can use any text editor, I suggest to use the Manifest Generator utility (ecmangen.exe). And here it comes the first caveat: although the documentation states that the SDK Include directory must be part of the INCLUDE environment variable, I am probably doing something wrong because no manifest file could be opened or created successfully; the Manifest Generator fails with the following error: "Center window failed to initialize. Error: 0x80070006. The handle is invalid.". What I did was to point the Manifest Generator "Start in" shortcut property to "C:\Program Files\Microsoft SDKs\Windows\v6.1\Include\" (the Microsoft Windows SDK default path installation) and it started to work.

With the Manifest Generator utility and according to your needs for providing ETW information, create your manifest file. To demo the next steps I supply a sample manifest file (Simple.Manifest.xml) that you can see here.

The Simple manifest file defines the SimpleEtwProvider with ID {d9b453b9-6230-486c-8dec-c7c5a2230d04}. The .NET initialization should look like this:

 using Etw = System.Diagnostics.Eventing;
//
// Code omitted intentionally...
//
Guid etwProviderId = new Guid("{d9b453b9-6230-486c-8dec-c7c5a2230d04}");
Etw.EventProvider provider = new Etw.EventProvider(etwProviderId);

Also, the Simple manifest file declares a SimpleEvent (ID and version), a SimpleOperationalChannel, a SimpleTask, a SimpleOpcode, and a SimpleKeyword. These must be used to create a ETW event descriptor as following:

 Etw.EventDescriptor descriptor;

unchecked
{
  descriptor = new Etw.EventDescriptor
  (
    0x3e9,                    // Event ID
    0x1,                      // Event Version
    0x10,                     // Operation Channel ID
    0x2,                      // Level
    0xa,                      // Opcode
    0x1,                      // Task
    (long) 0x8000000000000001 // Keywords
  );
}

The constituent parts of the event to be published are determined by the SimpleTemplate element in the manifest file: a StringData and an IntegerData. Data types and order must be observed when the event is published as shown below:

 // We know that the event has two parts: a string and an integer
// Be careful to provide the right types in the right order...
object[] payload = { "An event with manifest...", 98 };
provider.WriteEvent(ref descriptor, payload);

A recommended practice is to check if the provider is enabled (at least one session like Performance Monitor) and listening to what you publish. This can be accomplished as following:

 if(provider.IsEnabled())
{
  // We know that the event has two parts: a string and an integer
  // Be careful to provide the right types in the right order
  object[] payload = { "An event with manifest...",  98 };
  provider.WriteEvent(ref descriptor, payload);
}

That's it for the development part. Now let's go for the compilation part. First, for the manifest file you need to use the Windows Message Compiler (mc.exe) like this:

 MC Simple.Manifest.xml

And I found another caveat here. It happens that both utilities, Manifest Generator and Windows Message Compiler, depend on the winmeta.xml to be located through the INCLUDE environment variable. But even when everything seems to be in place, MC fails with error: ".\winmeta.xml(0) : error Failed trying to parse file.  result=0x800c0005: At Line=0, Column=0, The system cannot locate the resource specified.". I even tried the MC –W command-line switch but that didn't work either. I'm probably doing something wrong but to make it work, I had to copy winmeta.xml to the same location of Simple.Manifest.xml.

A successful MC compilation generates four files: MSG00001.bin, Simple.Manifest.h, Simple.Manifest.rc, and SimpleTEMP.BIN. The only interesting and useful, at least from the .NET developer perspective, is Simple.Manifest.rc. This is to be compiled with the Resource Compiler (rc.exe) to generate the corresponding Simple.Manifest.res as shown below:

 RC Simple.Manifest.rc

The last part is to embed the Simple.Manifest.res file into the ETW provider binary image. Let's assume that the .NET code shown above is a console application EtwProvider.cs, the compilation would look like the following:

 CSC /win32res:Simple.Manifest.res EtwProvider.cs

From inside the Visual Studio 2008 IDE, the .res file can be specified through the Resource File field in the Project Properties Application tab.

That's it for the compilation part. Now let's go for the deployment part. The full path from where the application will be executed in the target machine must be supplied in the messageFileName and resourceFileName attributes of the <provider> element in the manifest file (Simple.Manifest.xml). If, for example, the EtwProvider.exe is installed and executed from "C:\Program Files\" the manifest file should be modified to look like the following (some attributes and declarations were omitted intentionally for clarity sake):

 <?xml version="1.0" encoding="UTF-16"?>
<instrumentationManifest>
  <instrumentation>
    <events>
      <provider messageFileName="C:\Program Files\EtwProvider.exe"
                resourceFileName="C:\Program Files\EtwProvider.exe">

The modified manifest file must be registered in the target machine through the Windows Event Command-line utility (wevtutil.exe) as shown below:

 WEVTUTIL im Simple.Manifest.xml

Now we're ready to start publishing ETW events. In my next article I will show how to create an Event Trace Session through Performance Monitor.