Creating and publishing events using ETW Manifest-based provider

13 בפברואר 2011

one comment

ETW is a powerful and high performance tracing facility. In this post I will describe how to create your own ETW provider and publish events from you application. This post assumes you have a previous understanding of ETW concepts so I won’t cover in detail what ETW is and why to use it. If you’re an ETW newbie, then I invite you to ceck out Ran Wahle and myself at the SDP or to read this MSDN magazine article: Improve Debugging And Performance Tuning With ETW.

Before we start I’d like to go over this one point:

ETW provides two provider models:

  • Classic provider – This pre-Vista API has the following characteristics:
    • defines its events only in code.
    • must implement the registration and deregistration to trace sessions logic.
    • can be enabled by only one session.
  • Manifest-based provider – for providers that run on Vista or higher OS the following improvements are available:
    • allows resources and localization mapping capabilities (This is useful since the mapping is done during the interpretation of the trace to save the write overhead).
    • can be enabled by up to eight sessions.

Creating a Manifest-based provider is not a complicated task. It’s just a little bit long and a poorly documented process. In this guide I’ll demonstrate how to enable event tracing using the Manifest-based provider.

The Manifest

The first step when creating a Manifest-based ETW provider is to create a manifest file (naturally…). To do so you can either write the xml yourself and a minimalistic documentation about the manifest schema exists in msdn or use the Manifest Generator (ecmangen.exe) provided with the Microsoft Windows SDK as I do:

  • Select “new provider” and you should see the following form

Manifest, new evet, ETW, ecmangen.exe, provider

Most of this form is pretty self explanatory: the human readable name of the provider, a symbol that allows access to the provider from other manifests (I used the providers name here as well) and a Guid  that we will use to identify the provider from now on. The decoding files are something we should pay attention for:

These files are manifested assemblies that allow the mapping of localized messages and resources while interpreting the trace. Note however that using such files requires a full path, so no relative paths at this time.

  • The next step is to create an event in the manifest by selecting the “new event” option in the right hand sidebar. You should see the following form: 

Manifest, new evet, ETW, ecmangen.exe, provider

This is also pretty self explanatory for most parts: We give the event a symbol, an ID and a version. As for the message this is a localized message in en-US, to add more locals you will need to edit the manifest’s xml.

  • Next we will define a template. Templates describe the payload of the event which is an int in our case.

Manifest generator, new template screen

We create a template named MyEventData and a parameter named ReturnNumber to output a simple counter. Click  “add” and save the template and now we can go back to our event and add the template.

Manifest generator, event screen

  • Now we will create a channel where the provider can publish to:

Manifest generator, channel definition screen 

We define a name, symbol and a type (we can choose between admin, operational, admin and debug). and go back to the event to select the channel. Now we can also select the Level, Task, Opcode and Keywords that are all additional filtering options

Save the event, MyEvent now looks like this:

Manifest generator, event screen

Generating a header file using the Message Compiler

Our next step would be to use the Message Compiler (MC.exe) to generate a header file. using the following command line:

mc.exe [our manifest.man] –h [output path]

You’ll find in the output path a .h file that we will use shortly

Little bit of code

Lets put the manifest aside for a moment and now we can write some code:

  • The first step is to create an event provider and passing the same Guid we used in the manifest to its ctor:
var provider = new EventProvider(new Guid("{1B22749B-5EE3-49B5-9C1F-83AA56D393D6}"));

  • The next step is to create an event descriptor which represents the event we have defined in the manifest. We need to initialize the event provider with the Event id, version, channel, opcode, task and keywords we have defined in the manifest. To do so we can open the header file we’ve created with the MC and look for the following section:
//
// Event Descriptors
//
EXTERN_C __declspec(selectany) const EVENT_DESCRIPTOR MyEvent = {0x7d1, 0x1, 0x0, 0x4, 0x0, 0x0, 0x8000000000000000};

  • Now we can create an event descriptor like this:
EventDescriptor descriptor;
unchecked
{
    descriptor = new EventDescriptor(0x7d1,     // Event Id
                                     0x1,       // Event version
                                     0x10,       // Channel
                                     0x4,       // Level
                                     0x0,       // Opcode
                                     0x0,       // Task
                                     (long)0x8000000000000000); // Keywords
}

Note that The keyword is an unsigned long value and cannot be cast directly to a long so we call the ctor inside an unchecked block.

  • Now we can write the events:

We will request the user to enter the number of events to be written, then loop that many times, and write the event with the iteration number as default.

Console.WriteLine("Please enter the number of events to be written and press enter");
var returns = int.Parse(Console.ReadLine());

for (int i = 0; i < returns; i++)
{
    // we pass the event descriptor by ref and the counter
    // as our payload. Please note that the payload is passed
    // as params object[] so we can enjoy some good ol' boxing
    provider.WriteEvent(ref descriptor, i);
}

Compilation

Compiling a provider takes a few steps of its own:

  • We will now use the message compiler once again and run it with the following arguments:

mc.exe [our manifest.man] –r [output path]

This will output the two bin files and an rc file

  • We will now use the Resource Compiler (rc.exe) to generate a resource file out of the rc file we’ve just created:

rc.exe [our recource.rc]

  • We can now add the resource file to our visual studio project in the project properties application tab like this:

Project properties screen

and build our project.

Deployment
  • The next step will be to place our resource files (which is the entire application in our case at the location we have given them in the manifest.
  • We now need to register our provider using the wevtutil:

wevtutil im [our manifest.man]

And now our provider is installed as we can see by running xpref

xperf -providers i

XPerf 

and what’s left now is starting a new session and writing them events.

As you can see enabling ETW is not that hard at all and can come in handy when trying to understand the inner workings of your application.

That’s it for now

Yaniv

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

one comment

  1. http://www.watchesdealworld.com/7 בספטמבר 2013 ב 23:37

    You should always have a folder which contains all the resources for the project, Such as any fonts used in the graphics or font-face, as 2 years down the line and you need to make changes to a graphic and cant find the font can be a real pain. Also a .txt file with the url’s to any stock photography used ready for when you have to buy it.

    Reply