Supporting WPP Tracing

Windows Software Trace PreProcessor (WPP), provides an efficient real-time event logging mechanism. WPP is the recommended way to log trace and error messages in a WPD driver.

WPP traces include the system timestamp and can be used to measure performance, for example, by measuring the elapsed time between function calls.

Sample Drivers that Support WPP Tracing

The following sample drivers show how to use WPP tracing:

  • WpdBasicHardwareDriver
  • WpdServiceSampleDriver

Transitioning from OutputDebugString to WPP

The WpdHelloWorldDriver, WpdWudfSampleDriver, and the WpdMultiTransportDriver log error messages by using a CHECK_HR function that wraps around the OutputDebugString method. While this method is easy to use during development, it requires an active debugger connection in order to view the traces. The OutputDebugString method should be used minimally in final code for a release. WPP tracing is much more lightweight, flexible, and preferable for a range of tracing applications: logging errors for diagnosing failures, tracing code execution during development, for example.

In order to transition a driver from using OutputDebugString to use WPP, complete the following steps:

  1. Remove the original CHECK_HR() function definition (it can be found in WpdHelloWorldDriver.cpp or WpdWudfSampleDriver.cpp for the WPD samples) and its declaration in Stdafx.h.
  2. Update the Stdafx.h file with the WPP macros, which are described later in this topic.
  3. Add a RUN_WPP directive to the source files for your driver.
  4. Add the WPP initialization and cleanup routines to DllMain method for your driver.
  5. For each driver source file, add a #include <filename>.tmhafter the existing include statements.
  6. Rebuild your driver. Because you replaced the CHECK_HR function with an equivalent WPP macro that has the same signature, no changes are needed for any code that uses CHECK_HR.

The WPP preprocessor processes Stdafx.h for tracing macros, and generates a Trace Message Header (tmh) file for each .CPP file in the obj folders.

If you inspect the preprocessor output for each .CPP file that calls CHECK_HR (generated by using make WpdBaseDriver.pp), you notice that all CHECK_HR trace statements have been wrapped by WPP function calls, while the tracing text and WPP functions are defined in WpdBaseDriver.tmh.

The most common WPP-related compilation errors you can encounter are due to mismatched format identifiers and parameters (for example, if your format string contains an extra %d that does not match an argument) or missing entries in the WPP_CONTROL_GUIDS macro.

Updating the Stdafx.h File

The following code example contains the updates to make to Stdafx.h if your driver currently supports OutputDebugString.

#define MYDRIVER_TRACING_ID      L"Microsoft\\WPD\\ServiceSampleDriver"

#define WPP_CONTROL_GUIDS \
    WPP_DEFINE_CONTROL_GUID(ServiceSampleDriverCtlGuid,(f0cc34b3,a482,4dc0,b978,b5cf42aec4fd), \
        WPP_DEFINE_BIT(TRACE_FLAG_ALL)                                      \
        WPP_DEFINE_BIT(TRACE_FLAG_DEVICE)                                   \
        WPP_DEFINE_BIT(TRACE_FLAG_DRIVER)                                   \
        WPP_DEFINE_BIT(TRACE_FLAG_QUEUE)                                    \
        )

#define WPP_LEVEL_FLAGS_LOGGER(lvl,flags) \
           WPP_LEVEL_LOGGER(flags)

#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) \
           (WPP_LEVEL_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl)

//
// This comment block is scanned by the trace preprocessor to define our
// TraceEvents function.
//
// begin_wpp config
// FUNC Trace{FLAG=TRACE_FLAG_ALL}(LEVEL, MSG, ...);
// FUNC TraceEvents(LEVEL, FLAGS, MSG, ...);
// end_wpp

//
// This comment block is scanned by the trace preprocessor to define our
// CHECK_HR function.
//
//
// begin_wpp config
// USEPREFIX (CHECK_HR,"%!STDPREFIX!");
// FUNC CHECK_HR{FLAG=TRACE_FLAG_ALL}(hrCheck, MSG, ...);
// USESUFFIX (CHECK_HR, " hr= %!HRESULT!", hrCheck);
// end_wpp

//
// PRE macro: The name of the macro includes the condition arguments FLAGS and EXP
//            define in FUNC above
//
#define WPP_FLAG_hrCheck_PRE(FLAGS, hrCheck) {if(hrCheck != S_OK) {

//
// POST macro
// The name of the macro includes the condition arguments FLAGS and EXP
//            define in FUNC above
#define WPP_FLAG_hrCheck_POST(FLAGS, hrCheck) ; } }

//
// The two macros below are for checking if the event should be logged and for
// choosing the logger handle to use when calling the ETW trace API
//
#define WPP_FLAG_hrCheck_ENABLED(FLAGS, hrCheck) WPP_FLAG_ENABLED(FLAGS)
#define WPP_FLAG_hrCheck_LOGGER(FLAGS, hrCheck) WPP_FLAG_LOGGER(FLAGS)

Adding the WPP Initialization and Cleanup Routines to DllMain

After you update the Sources file, update the DllMain routine for the driver, as shown in the following code example:

// DLL Entry Point
extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{    
if(DLL_PROCESS_ATTACH == dwReason)    
{        
g_hInstance = hInstance;              
//        
// Initialize tracing.        
//        
WPP_INIT_TRACING(MYDRIVER_TRACING_ID);    
}    
else if (DLL_PROCESS_DETACH == dwReason)    
{        
//        
// Cleanup tracing.        
//        
WPP_CLEANUP();    
}    
return _AtlModule.DllMain(dwReason, lpReserved);
}

Including the Trace Message Header files

After you update the DllMain routine, update each .CPP source file and include the corresponding trace-message header file. The following code example shows the inclusion of the WpdBaseDriver.tmh file in WpdBaseDriver.cpp.

//
// WpdBaseDriver.cpp
//#include "stdafx.h"
#include "WpdBaseDriver.h"
// Include the WPP generated Trace Message Header (tmh) file for this .cpp
#include "WpdBaseDriver.tmh"

WDK and Visual Studio build environment