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 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
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:
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.
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.
- Now we will create a channel where the provider can publish to:
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:
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:
- 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:
- Now we can create an event descriptor like this:
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.
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:
and build our project.
- 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
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