OSRLogo
OSRLogoOSRLogoOSRLogo x OSR Custom Development Services
OSRLogo
x

Everything Windows Driver Development

x
x
x
GoToHomePage xLoginx
 
 

    Thu, 14 Mar 2019     118020 members

   Login
   Join


 
 
Contents
  Online Dump Analyzer
OSR Dev Blog
The NT Insider
The Basics
File Systems
Downloads
ListServer / Forum
  Express Links
  · The NT Insider Digital Edition - May-June 2016 Now Available!
  · Windows 8.1 Update: VS Express Now Supported
  · HCK Client install on Windows N versions
  · There's a WDFSTRING?
  · When CAN You Call WdfIoQueueP...ously

WPP Tracing Part I -- Supporting Windows 2000 and Beyond

If you're here, then you should know what WPP tracing is already. If you don't, here's a quick overview:

WPP tracing is a mechanism that was added to Windows to allow fast tracing of debug messages to memory or disk for later retrieval. The things that make WPP tracing neat are its extremely low overhead and its ability to separate the format of the trace messages from the data, allowing you to keep debug messages in the free build of your driver while preserving your intellectual property.

Told you it would be quick. Now, the true audience of this series of articles is the poor souls that have been struggling with the same problem that I have: getting WPP tracing to work in a single binary on Windows 2000, XP, and Server 2003.

The Goal
I started out with what appeared to be a simple goal: all I wanted to do was add WPP tracing to my USB driver that supports Windows 2000 through Windows Server 2003. I had done this same thing a bunch of times before (well, at least I thought I had) and, heck, I knew there was a tracing sample in the DDK if I ran into any problems. In fact, I was so confident in my ability to add WPP tracing to a driver that I was preparing to finally write the WPP tracing article that I hadn't been able to get anyone else to write. But, as I was about to find out, this driver supported something that made it different than the other drivers that I had added WPP tracing to before: WMI.

Don't Have WMI Support In Your Driver?
If you don't use any WMI in your driver, you should think about adding it. But that's another article for another day and I won't leave you out in the cold. What you should know is that you are going to have fewer details to worry about when adding WPP support to your driver. Therefore, this article is going to cover what needs to be done to get WPP working for those of you that don't have to worry about WMI. This is much trickier than it should be, and an article on the topic in The NT Insider is long overdue.

The lucky ones can stop at the end of this article and get a latte, but the rest of you should read Part II.  Part II will dive into the extra kludges that you will need to have WPP and WMI coexist in the same driver.

Goals and Non-Goals
The goal of this article is to show how to use one binary to support WPP tracing on Windows 2000 through Windows Server 2003. During the course of this discussion, we may also bring up how that differs from adding tracing to support only XP and later. However, the currently available samples do a fine job of showing XP and later support so we'll let them do what they do. Also, there's really no point in talking about the newer, gentler WPP tracing when most of us still have to support that legacy O/S known as Windows 2000.

Now for everyone's favorite, the non-goals. This article will assume that you're already familiar with WPP tracing and the tools used to capture WPP trace messages. If you need a refresher in these topics, you can check out previous articles from The NT Insider such as Without A Trace? Event Tracing For Windows and New Tracing Features For Windows (both found in the Jan-Feb 2003 issue).

$(SOURCES) By passing this as a parameter, we're passing each of the source files that we're building with the SOURCES files.
-km This parameter indicates that we're building WPP tracing into a kernel mode component.
-gen:{km-w2k.tpl}*.tmh

Because we are targeting Windows 2000, we need to indicate to the trace preprocessor that it needs to use the Windows 2000 tracing template to generate the TMH files (what we do with these is explained later). This parameter can be omitted if you are only targeting Windows XP and later.

-dll If you are using the .CPP suffix for your driver (that is of course written in C), you will need to pass this parameter.
-func:OsrTracePrint(LEVEL, EVENT, (MSG,...)) The func option allows us to indicate which function we would like to use for our WPP trace statements. You will see here that we are also specifying that we would like to use levels and events for our trace statements. We will discuss this further later in the document.

Table 1 -- SOURCES parameters

How to Add Tracing
What will follow is a how-to of sorts on getting WPP tracing to work in your driver. There's a lot of magic and not much documentation on WPP, so my suggestion would be to just follow the steps outlined in this article. If you're really interested in the gory details, hopefully the discussion of how we solved some of our tracing woes in Part II will give you a head start.

SOURCES File Changes
The first thing that you are going to need to do is add a RUN_WPP directive to your SOURCES file.

RUN_WPP=$(SOURCES)             \
        -km                    \
        -gen:{km-w2k.tpl}*.tmh \
        -dll                   \
        -func:OsrTracePrint(LEVEL,EVENT,(MSG,...))

This directive indicates to the build process that it should run the WPP tracing trace preprocessor (tracewpp.exe), passing it the supplied parameters. Table 1 discusses each of the parameters to the directive.

Once you have added the RUN_WPP directive to your SOURCES file, you can move on to modifying your source files to support WPP.

Main Include File Changes
In a header file that is included by all of your modules you will need to make the following changes:

Defining a Control GUID
WPP tracing works on the concept of trace providers and trace consumers. It's exactly how it sounds: providers generate trace messages and consumers gather, and potentially format, the messages. Each trace provider in the system uniquely identifies itself by way of a GUID. Because your driver is going to become a trace provider, it needs to define a new GUID under which it will register itself.

The driver is responsible for defining this control GUID and also for defining the types of messages that are available through this control GUID.

#define WPP_CONTROL_GUIDS \
WPP_DEFINE_CONTROL_GUID(OSRWPP,(AC072299,57FA,46e9,BF59,E9E649182794),\
    WPP_DEFINE_BIT(OSRDBG_PNP_INFO)           \
    WPP_DEFINE_BIT(OSRDBG_IOCTL_INFO)         \
    WPP_DEFINE_BIT(OSRDBG_POWER_INFO)         \
    WPP_DEFINE_BIT(OSRDBG_READWRITE_INFO)     \
    )

Here we have created one control GUID, with a GUID generated by the GUIDGen.exe utility, and we have also defined four different types of tracing events available through this GUID: PnP events, IOCTL events, Power events, and Read/Write events. These events will be passed as the EVENT parameter to our OsrTracePrint function.

NOTE: Because WPP keeps track of the different types of tracing events in a 32bit long bitmask, only 32 different events can be assigned to a control GUID.  If you require more, it's possible to creat multiple control GUIDs.

Support for Levels
Most, if not all, samples that you will find only show how to use the events created by the WPP_DEFINE_BIT macro in your tracing statements. However, it's extremely useful to be able to support a verbosity level for your tracing messages. This allows you to indicate in your tracing messages if the particular message is an error, a warning, or just verbose output. Because this requires the creation of a custom trace function, two other macros need to be added to your include file to properly add levels support to your trace messages:

#define WPP_LEVEL_EVENT_LOGGER(lvl,event) \
    WPP_LEVEL_LOGGER(event)

#define WPP_LEVEL_EVENT_ENABLED(lvl, event) \
    (WPP_LEVEL_ENABLED(event) && WPP_CONTROL(WPP_BIT_ ##event).Level >= lvl)

The last thing that you need to do for levels support is define the levels. It is strongly recommended that you do not define your own levels, but use the ones defined by the system. The definitions of the levels can be found in evntrace.h in the Platform SDK:

#define TRACE_LEVEL_NONE        0 // Tracing is not on
#define TRACE_LEVEL_FATAL       1 // Abnormal exit or termination
#define TRACE_LEVEL_ERROR       2 // Severe errors that need logging
#define TRACE_LEVEL_WARNING     3 // Warnings such as allocation failure
#define TRACE_LEVEL_INFORMATION 4 // Includes non-error cases
#define TRACE_LEVEL_VERBOSE     5 // Detailed traces from intermediate steps
#define TRACE_LEVEL_RESERVED6   6
#define TRACE_LEVEL_RESERVED7   7
#define TRACE_LEVEL_RESERVED8   8
#define TRACE_LEVEL_RESERVED9   9

Changes to Driver Files
You will now need to make a few changes to your driver source files.

Including the TMH Files
Do you remember that you told the build process to generate TMH files by adding the RUN_WPP directive to your SOURCES file? No? Well, you did, and once you build your driver there will be one TMH file per module. In each of your source files you will need to include the corresponding TMH file, and remember to wrap the include in extern "C" if using the .CPP suffix:

//
// osrwpp_pnp.cpp
//
#include "osrwpp.h"
extern "C" {
#include "osrwpp_pnp.tmh"
}

DriverEntry Changes

I can hear the war torn WPP veterans out there grumbling about including some macro in DriverEntry in order to support Windows 2000 tracing (remember, we're not interested in XP-style tracing as the Windows 2000 style works across all O/S's). Well, this is the macro that they are grumbling about:

WPP_SYSTEMCONTROL(DriverObject);

Sorry to say it, but I have bad news for anyone out there that has this macro in their code: You're sitting on a time bomb. Try running your WDM driver built with this macro in it on a Server 2003 system with Verifier enabled. You'll quickly hit a Verifier error due to an IRP_MJ_SYSTEM_CONTROL IRP not being properly forwarded. If you look at the definition of this macro, you'll see that all it does is assign your driver object's IRP_MJ_SYSTEM_CONTROL handler to WppSystemControl, which, as it turns out, is the routine that's causing the Verifier error. I don't know about you, but if my driver doesn't pass Verifier I consider it broken and so using this macro is now out.

So, contrary to many of the samples that you will find, you should not call the WPP_SYSTEMCONTROL macro and therefore, there are no changes to make to DriverEntry.

NOTE: For an in-depth description of what causes this problem, see Part II of this article.

IRP MJ_SYSTEM_CONTROL Handler Changes
There are, however, changes that need to be made to IRP_MJ_SYSTEM_CONTROL. Now, you should already have an IRP_MJ_SYSTEM_CONTROL handler even if you don't support WMI, because it's a requirement of all WDM drivers. I'm sure that you have a SystemControlHandler and that it looks something like the following:

NTSTATUS
OsrSystemControl(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    ) {

    POSR_DEVICE_EXT devExt  = (POSR_DEVICE_EXT)DeviceObject->DeviceExtension;

    //
    // Because we don't play WMI, pass this request on down to the
    // driver below us. Theory being that just ?cause we don't
    // do WMI doesn't mean we should prevent drivers below
    // us from doing it...
    //
    IoSkipCurrentIrpStackLocation(Irp);

    return IoCallDriver(devExt->DeviceToSendIrpsTo, Irp);

}

What we need to do here is add the code to call into WPP when our driver is being called by WMI (remember that WPP is realized through the use of WMI). So, the new OsrSystemControl would look something like this:

NTSTATUS
OsrSystemControl(
    PDEVICE_OBJECT DeviceObject,
    PIRP Irp
    ) {

    POSR_DEVICE_EXT devExt  = (POSR_DEVICE_EXT)DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION ioStack = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS status;
    ULONG bytesReturned = 0;
   
    if (DeviceObject == (PDEVICE_OBJECT)ioStack->Parameters.WMI.ProviderId) {

        //
        // Our device is being sent a WMI request. Though we don't
        //  specifically support anything WMI, we DO use WPP
        //  tracing on Windows 2000. For reasons laid out in that
        //  fantastically amazing article on WPP tracing in
        //  The NT Insider, we must pass the request on to
        //  WPP through the WPP_TRACE_CONTROL macro.
        //

        if (ioStack->MinorFunction != IRP_MN_REGINFO_EX) {

               

            if (ioStack->MinorFunction == IRP_MN_REGINFO) {

 

                //

                // WHY are we zeroing this buffer? See Part II of

                //  this article!

                //

                RtlZeroMemory(ioStack->Parameters.WMI.Buffer,

                              ioStack->Parameters.WMI.BufferSize);

 

            }

 

            //

            // NOTE: This is one of those annoying macros that
            //  turns your pass by value into a pass by reference,
            //  which is why we just pass bytesReturned and not
            //  &bytesReturned
            //

           
status = WPP_TRACE_CONTROL(ioStack->MinorFunction,

                                       ioStack->Parameters.WMI.Buffer,

                                       ioStack->Parameters.WMI.BufferSize,

                                       bytesReturned);

 

        } else {

 

            //

            // Fail any IRP_MN_REGINFO_EX requests that we receive.

            //  This is yet another a critical component of making this work

            //  on Windows 2000, see Part II for all the answers.

            //

            status = STATUS_UNSUCCESSFUL;

        }

 

        Irp->IoStatus.Status = status;

        Irp->IoStatus.Information = bytesReturned;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);
    }

    //
    // Otherwise this is not for us. Just pass it down
    //
    IoSkipCurrentIrpStackLocation(Irp);

    return IoCallDriver(devExt->DeviceToSendIrpsTo, Irp);

}

 

Registering Your Devices with WPPI
To support Windows 2000 tracing, you will need to call the WPP_INIT_TRACING macro on your device objects after you have successfully forwarded your IRP_MJ_PNP/IRP_MN_START_DEVICE IRP:

WPP_INIT_TRACING(DeviceObject, &OsrRegistryPath);

Notice that the WPP_INIT_TRACING macro takes a pointer to the registry path that was passed to you during DriverEntry, so you will need to make a copy of that registry path before leaving DriverEntry.

You must also call WPP_CLEANUP on your device objects before you delete them.

WPP_CLEANUP(DeviceObject);

Add Tracing Statements
Here it is, the moment that you have all been waiting for--The last thing that you need to do in your driver is begin adding tracing statements. In your IRP_MJ_READ dispatch entry point you might add messages such as:

OsrTracePrint(TRACE_LEVEL_VERBOSE, OSRDBG_READWRITE_INFO,
("OsrRead Entered...\n"));

Or:

if (Read is too large)
    OsrTracePrint(TRACE_LEVEL_ERROR, OSRDBG_READWRITE_INFO,
("OsrRead Length of the read exceeds max\n"));

Or any other messages that you wish to have as part of your tracing scheme.

NOTE:  Just to once again remind you that I feel your pain, I also hate all of these magic macros that appear out of nowhere. Part II of the article will explain the genesis of these findings and a bit more about how WPP effects the build environment and your driver in general.

That's All!
It's unfortunate that getting tracing to work for Windows 2000 is such a mystery, but hopefully with the guidance of this article you will be up and running with WPP in no time.

NOTE: If you happen to be conditionally compiling your driver to work for XP and Windows 2000, you might notice that the WPP_INIT_TRACING macro on XP and later takes a pointer to your driver object instead of a device object. Therefore, you only need to call the macro once during DriverEntry processing. There is something to watch out for here that tripped me up for a while: the WPP preprocessor will not tolerate the WPP_INIT_TRACING macro being in more than one module even if it is conditionally compiled out of one of the modules. So, if you call WPP_INIT_TRACING in your DriverEntry in one module if compiling for XP, and call WPP_INIT_TRACING in your PnP module if compiling for Windows 2000, you will get compiler errors due to multiple definitions. The only solution is to add a separate module that exports functions that call the macros.

Need to Support WMI? Annoyed with Magic Macros and No Explanations?
Then you're going to love Part II of this article, I know I just loved going through the experience that prompted me to write it....

User Comments
Rate this article and give us feedback. Do you find anything missing? Share your opinion with the community!
Post Your Comment

"Then there are minifilters..."
Good stuff! However, I should point out that there is even more guesswork, experimentation, and frustration involved when using WPP with minifilters, which use the new Filter Manager. For example:

- Big-time conflicts between ntifs.h and ntddk.h, which gets invoked by WPP on Windows 2000

- WPP_INIT_TRACING for Windows 2000 requires a DeviceObject, which doesn't exist in minifilters

- Support for IRP_MJ_SYSTEM_CONTROL is optional for minifilters, so you have to handle that to support WPP

I have gotten all this to work successfully (there was a series of messages in the NTFSD forum entitled "WPP, Win2K and Minifilters" about them), but there was a lot of pain involved.

So be warned that much of the excellent advice in this article doesn't apply to minifilters.

Rating:
22-Mar-05, Kenneth Cross


Post Your Comments.
Print this article.
Email this article.
bottom nav links