Can I use WPP in a C++ macro?

I'm trying to figure out how can I include a WPP trace message in a macro.

Say, I'm using a vanilla WPP that is generated with the KMDF project. If I just do:

void func()
{
    TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "irql=%d", KeGetCurrentIrql());
}

It compiles just fine.

But then if I put it in a macro:

#define IS_PASSIVE_LEVEL()      \
    if(KeGetCurrentIrql() != PASSIVE_LEVEL)     \
    {                                           \
        TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "irql=%d", KeGetCurrentIrql());        \
        ExRaiseStatus(STATUS_UNSUCCESSFUL);                           \
    }

and then try using it:

void func()
{
    IS_PASSIVE_LEVEL;
}

I'm getting an error:

Main.cpp(698,5): error C2065: 'TRACE_DEVICE': undeclared identifier
Main.cpp(698,5): error C3861: 'WPP_CALL_Main_cpp698': identifier not found

What am I doing wrong there?

WPP has its own preprocessor that runs before the C/C++ one and so you can't wrap the trace statements in macros (annoying but been a known issue since the beginning).

The backwards way to do this is to wrap the trace message call in WPP PRE and POST macros that do what you want. It's kind of painful to get right the first time but once you have it it's handy (e.g. we do this to log failed ASSERTs into the WPP Recorder in the release build). Your above behavior could be implemented with something like this:

#define WPP_CONTROL_GUIDS                                      \
    WPP_DEFINE_CONTROL_GUID(                                   \
	...
        WPP_DEFINE_BIT(PASSIVE_ASSERTS)                   \
        )

//
// begin_wpp config
// 
// FUNC IS_PASSIVE_LEVEL{FLAG=PASSIVE_ASSERTS}();
//
// end_wpp
//

#define WPP_FLAG_PRE(PASSIVE_ASSERTS) \
    do {                                                             \
        if (KeGetCurrentIrql() != PASSIVE_LEVEL) {    

#define WPP_FLAG_POST(PASSIVE_ASSERTS) \
            /*DoTraceMessage*/;                                      \
            ExRaiseStatus(STATUS_UNSUCCESSFUL);                      \
        }                                                            \
    } while(0)

(That also might require some additional massaging and could possibly be simplified, I just threw it together for an example. WPP is always a bit finicky...)

1 Like

@Scott_Noone_OSR , thanks. But I'm still getting that same error. And yes. You bet, WPP is a finicky beast.

Let me show what I'm doing.

First off, I added your exact same declarations/definitions into the trace.h file.

Then the definitions of WPP_FLAG_PRE and WPP_FLAG_POST went into the .h file for the .cpp file where I was intending to use them. (But then I also tried to place them into trace.h. It didn't help.)

Finally I used those macros as such:

void func()
{
    WPP_FLAG_PRE(PASSIVE_LEVEL_CHECK);
    WPP_FLAG_POST(PASSIVE_LEVEL_CHECK);
}

And got these errors when compiling:

Main.cpp(3065,5): error C2065: 'TRACE_DEVICE': undeclared identifier|
|---|---|
Main.cpp(3065,5): error C3861: 'WPP_CALL_Main_cpp3065': identifier not found|

I'm still missing something.

PS. TRACE_DEVICE came from:

TraceEvents(TRACE_LEVEL_ERROR, TRACE_DEVICE, "irql=%d", KeGetCurrentIrql());

that I placed inside your WPP_FLAG_POST.

No, you call IS_PASSIVE_LEVEL() and not the macros themselves. This comment block is actually part of the WPP configuration:

//
// begin_wpp config
// 
// FUNC IS_PASSIVE_LEVEL{FLAG=PASSIVE_ASSERTS}();
//
// end_wpp
//

You should see that function defined in your TMH with the pre/post macro contents emitted as part of it.

Yeah, it still doesn't work. I'm not sure what I'm doing wrong. This stuff is super confusing. Is there a working example somewhere, do you know?

What doesn't work? It doesn't compile? Or it compiles but you don't see the expected trace? Or something else?

Here's a diff patch that adds what I put here into the tracedrv sample. I didn't run it but it compiles and seems to emit the right code based on the assembly. (As I style thing I don't like the WPP macro raising a status, but that's not the question at the moment...)

Windows-driver-samples/general/tracing/tracedriver/tracedrv at main · microsoft/Windows-driver-samples

diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.c b/general/tracing/tracedriver/tracedrv/tracedrv.c
index a1606f7b..0ca504bb 100644
--- a/general/tracing/tracedriver/tracedrv/tracedrv.c
+++ b/general/tracing/tracedriver/tracedrv/tracedrv.c
@@ -200,6 +200,9 @@ Return Value:
 
     PAGED_CODE();
 
+    IS_PASSIVE_LEVEL();
+
+
     IoCompleteRequest( Irp, IO_NO_INCREMENT );
     return STATUS_SUCCESS;
 }
diff --git a/general/tracing/tracedriver/tracedrv/tracedrv.h b/general/tracing/tracedriver/tracedrv/tracedrv.h
index 1b86834a..cae4b903 100644
--- a/general/tracing/tracedriver/tracedrv/tracedrv.h
+++ b/general/tracing/tracedriver/tracedrv/tracedrv.h
@@ -36,7 +36,8 @@ Environment:
 #define WPP_CONTROL_GUIDS \
     WPP_DEFINE_CONTROL_GUID(CtlGuid,(d58c126f, b309, 11d1, 969e, 0000f875a5bc),  \
         WPP_DEFINE_BIT(FLAG_ONE)                \
-        WPP_DEFINE_BIT(FLAG_TWO) )
+        WPP_DEFINE_BIT(FLAG_TWO)                \
+        WPP_DEFINE_BIT(PASSIVE_ASSERTS) )
 
 //
 // DoTraceLevelMessage is a custom macro that adds support for levels to the 
@@ -89,8 +90,19 @@ typedef enum _MachineState {
 //USEPREFIX (TRACE_RETURN, "%!STDPREFIX!");
 //FUNC TRACE_RETURN{FLAG=FLAG_ONE}(EXP);
 //USESUFFIX (TRACE_RETURN, "Function Return=%!STATUS!",EXP);
+// FUNC IS_PASSIVE_LEVEL{FLAG=PASSIVE_ASSERTS}();
 //end_wpp
 
+#define WPP_FLAG_PRE(PASSIVE_ASSERTS) \
+    do {                                                             \
+        if (KeGetCurrentIrql() != PASSIVE_LEVEL) {
+
+#define WPP_FLAG_POST(PASSIVE_ASSERTS) \
+            /*DoTraceMessage*/;                                      \
+            ExRaiseStatus(STATUS_UNSUCCESSFUL);                      \
+        }                                                            \
+    } while(0)
+
 //
 // PRE macro: The name of the macro includes the condition arguments FLAGS and EXP
 //            define in FUNC above

@Scott_Noone_OSR sorry, I got so frustrated with this WPP BS ... so I just dropped it.

Now that I calmed down and decided to give it a second try. I'm realizing that I'm probably not doing something obvious. To show it, I created a default KMDF project in VS 2022 and added your suggestions.

I'm still getting these errors when I try to compile it:

Driver.c(69,5): error C4013: 'WPP_RECORDER_FLAG_FILTER' undefined; assuming extern returning int
Driver.c(69,5): error C2065: 'PASSIVE_ASSERTS': undeclared identifier
Driver.c(69,5): error C4013: 'WPP_RECORDER_FLAG_ARGS' undefined; assuming extern returning int
Driver.c(69,5): warning C4022: 'WPP_RECORDER_SF_': pointer mismatch for actual parameter 1
Driver.c(69,5): warning C4047: 'function': 'ULONG' differs in levels of indirection from 'const GUID *'
Driver.c(69,5): warning C4024: 'WPP_RECORDER_SF_': different types for formal and actual parameter 3
Driver.c(69,5): error C2198: 'WPP_RECORDER_SF_': too few arguments for call

I made a copy of that project here. Can you please take a look? There are just a few modifications there.

Everything WPP related is tediously difficult to get right. My suggestion is to examine the tmh file to see exactly what is being emitted by the WPP preprocessor.

I tried checking tmh files but I'm not sure what am I supposed to find there.

@Scott_Noone_OSR can you please check?

If you don't want to download my sample, here's how trace.h looks after I modified it:

#define WPP_CONTROL_GUIDS                                              \
    WPP_DEFINE_CONTROL_GUID(                                           \
        KMDFWPPTest1TraceGuid, (9520439d,9ecf,44dd,9ada,59b614f42986), \
                                                                            \
        WPP_DEFINE_BIT(MYDRIVER_ALL_INFO)                              \
        WPP_DEFINE_BIT(TRACE_DRIVER)                                   \
        WPP_DEFINE_BIT(TRACE_DEVICE)                                   \
        WPP_DEFINE_BIT(TRACE_QUEUE)                                    \
        WPP_DEFINE_BIT(PASSIVE_ASSERTS)                   \
        )                             

#define WPP_FLAG_LEVEL_LOGGER(flag, level)                                  \
    WPP_LEVEL_LOGGER(flag)

#define WPP_FLAG_LEVEL_ENABLED(flag, level)                                 \
    (WPP_LEVEL_ENABLED(flag) &&                                             \
     WPP_CONTROL(WPP_BIT_ ## flag).Level >= level)

#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)

//           
// WPP orders static parameters before dynamic parameters. To support the Trace function
// defined below which sets FLAGS=MYDRIVER_ALL_INFO, a custom macro must be defined to
// reorder the arguments to what the .tpl configuration file expects.
//
#define WPP_RECORDER_FLAGS_LEVEL_ARGS(flags, lvl) WPP_RECORDER_LEVEL_FLAGS_ARGS(lvl, flags)
#define WPP_RECORDER_FLAGS_LEVEL_FILTER(flags, lvl) WPP_RECORDER_LEVEL_FLAGS_FILTER(lvl, flags)

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


#define WPP_FLAG_PRE(PASSIVE_ASSERTS) \
    do {                                                             \
        if (KeGetCurrentIrql() != PASSIVE_LEVEL) {    

#define WPP_FLAG_POST(PASSIVE_ASSERTS) \
            /*DoTraceMessage*/;                                      \
            ExRaiseStatus(STATUS_UNSUCCESSFUL);                      \
        }                                                            \
    } while(0)

And then I use the IS_PASSIVE_LEVEL macro in Driver.c as such:

NTSTATUS
DriverEntry(
    _In_ PDRIVER_OBJECT  DriverObject,
    _In_ PUNICODE_STRING RegistryPath
    )
{
    WDF_DRIVER_CONFIG config;
    NTSTATUS status;
    WDF_OBJECT_ATTRIBUTES attributes;

    //
    // Initialize WPP Tracing
    //
    WPP_INIT_TRACING(DriverObject, RegistryPath);

    TraceEvents(TRACE_LEVEL_INFORMATION, TRACE_DRIVER, "%!FUNC! Entry");


    IS_PASSIVE_LEVEL();

  //....
}

This topic was automatically closed 60 days after the last reply. New replies are no longer allowed.