WPP_CALL_ undefined for static kernel library

I need some help fixing a WPP compiler error - specifically that the trace function is not found. I’ve been following every bit of guidance I can find (osr, MDSN, NTDev) and can’t seem to find the secret sauce. While I’ve been able get basic WPP working in a kernel driver, I’m now trying to add it to a static kernel library. I was trying to follow this MSDN page:

http://msdn.microsoft.com/en-us/library/windows/hardware/ff546863(v=vs.85).aspx

But that didn’t seem to help with the compiler error. Also, for the project I’m working on, it’s a large codebase and trying to include the libraries GUID and other info in a shared spot like the 2nd part of that MSDN page somewhat implies gets complicated.

Therefore I figured I would just try to get the static library to have it’s own (basic case) and then get fancy. I’m hoping someone with more WPP experience could spot where I went sideways. I’ve tried to make the relevant code generic enough to post here. I’m targeting the Win8 DDK - therefore MSBuild project vs some of the old sources/dirs. I’ve got my run_wpp to point at wpptrace.h and that’s about it. I cut/copied most of the settings from a known-working driver WPP implementation, but no dice.

Any help is appreciated.

---------------WPPTrace.h ---------

#pragma once

#define INITGUID
#include <evntrace.h> // Needed for WPP TRACE level defines
#include <initguid.h>

// defined values use to send WPP traces directly to DbgPrint

#define MODULE_NAME mySvcLib
#define MODULE_NAME_STRING “mySvcLib”

// In order to send trace statements directly to the kernel
// debugger, the following two #defines are required. It is
// recommended to always include these.
//

#define WPP_AUTOLOGGER L"mySvcLib"
#define WPP_GLOBALLOGGER

//
// Define the tracing flags.
//
//

#define WPP_CONTROL_GUIDS <br> WPP_DEFINE_CONTROL_GUID( <br> MYSvcLibTraceGuid, (221E9D14, DF58, 402D, B9AB, F903D27FF575), <br> <br> WPP_DEFINE_BIT(MY_DBG_ERR) <br> WPP_DEFINE_BIT(MY_DBG_WARN) <br> WPP_DEFINE_BIT(MY_DBG_INFO) <br> WPP_DEFINE_BIT(MY_DBG_EVENT) <br> )

//
// To use levels and flag bits together to control tracing, the
// following two #defines are required. By default, WPP tracing
// only uses the flag bits defined with the control GUID
//

#define WPP_LEVEL_FLAG_LOGGER(lvl,flags) WPP_LEVEL_LOGGER(flags)
#define WPP_LEVEL_FLAG_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
// Trace function.
//
// MACRO: TraceEvents
//
// begin_wpp config
// USEPREFIX (TraceEvents,“%!STDPREFIX!”);
// FUNC TraceEvents(LVL,FLG,MSG,…);
// end_wpp
//

#define WPP_LVL_FLG_PRE(LVL, FLG)
#define WPP_LVL_FLG_POST(LVL, FLG)
#define WPP_LVL_FLG_ENABLED(LVL, FLG) WPP_LEVEL_FLAG_ENABLED(LVL,FLG)
#define WPP_LVL_FLG_LOGGER(LVL, FLG) WPP_LEVEL_FLAG_LOGGER(LVL,FLG)

//
// To handle NULL strings in trace arguements
//
#define WPP_CHECK_FOR_NULL_STRING

------ common_logging.h --------------------------------------

#pragma once

#include <ntddk.h>
#include <stdarg.h>
#include <evntrace.h> // Needed for WPP TRACE level defines

#include “wpptrace.h”

extern int MY_verbosity;
extern int MY_printk_level;

#define ERR 0x00000001
#define WARNING 0x00000002
#define INFO 0x00000004
#define DEBUG 0x00000008

INLINE void MY_vprintk(ULONG type, const char *fmt, va_list args)
{

{
TraceEvents(type,fmt,args);
}
}

---------------------- mylib.c ------------------

#include “wpptrace.h”
#include “mylib.tmh” // For WPP and ETW tracing

< more includes>

#include “common_logging.h”

----- error message ---------
1>common_logging.h(70): error C4013: ‘WPP_CALL_mylib_c70’ undefined; assuming extern returning int</evntrace.h></stdarg.h></ntddk.h></initguid.h></evntrace.h>

Hi Eric. Welcome to NTDEV.

Hmmm… Does the library itself build correctly? WITHOUT WPP tracing.

There’s a bug in the WDK that’s been discussed here in which if you try to build a static DRIVER library, you wind up getting a status USER MODE library instead. The work around is to create an empty WDM driver project and then modify the target type to static library.

??

Peter
OSR
@OSRDrivers

Peter -
Thanks. Yes, the library has build/been running for quite some time with dbgprint. I’ve wanted to change that over to WPP and ETW. WPP was the first step and it is full of fail.

Since you indirectly touched on it, here’s my build output (file names sanitized, settings left alone) to show the settings when the lib gets built. I first created this library about a year ago and think I started w/a wdm, but ultimately the settings will clarify where I might’ve gone sideways.

Also, is there a link somewhere to the WDK bug you mentioned? My google skillz are apparently requiring more caffeine to return useful results.

Appreciate the help.
-Eric

Output for debug build:

RunWpp:
C:\Program Files (x86)\Windows Kits\8.0\bin\x86\tracewpp.exe -ext:.c.h /I…....\common;“…....\myLib”;“…....\common\include”; -cfgdir:“C:\Program Files (x86)\Windows Kits\8.0\bin\wppconfig\rev1” -scan:“wpptrace.h” -odir:“x64\Win8Debug\” -km -dll mylib.c

ClCompile:
C:\Program Files (x86)\Microsoft Visual Studio 11.0\VC\bin\AMD64\CL.exe /c /Ix64\Win8Debug\ /Ix64 \Win8Debug\ /I…....\common /I…\include /I"C:\Program Files (x86)\Windows Kits\8.0\Include\km" /I…\myDriver\inc /Zi /nologo /W4 /WX /Od /Oi /Oy- /D _WIN64 /D AMD64 /D AMD64 /D _WIN64 /D AMD64 /D AMD64 /D DEPRECATE_DDK_FUNCTIONS=1 /D MSC_NOOPT /D WIN32_LEAN_AND_MEAN=1 /D _WIN32 _WINNT=0x0602 /D WINVER=0x0602 /D WINNT=1 /D NTDDI_VERSION=0x06020000 /D DBG=1 /D _KERNEL /D WPP_TRACING_ENABLED /GF /Gm- /Zp8 /GS /Gy /fp:precise /Zc:wchar_t- /Zc:forScope- /GR- /Fo"x64\Win8Debug\" /Fd"x64\Win8Debug\vc110.pdb" /Gz /wd4748 /wd4603 /wd4627 /wd4986 /wd4987 /wd4996 /FI"C:\Program Files (x86)\Windows Kits\8.0\Include\Shared\warning.h" /errorReport:queue /kernel /kernel -cbstring /homeparams -d2epilogunwind /d1 import_no_registry /d2AllowCompatibleILVersions /d2Zi+ mylib.c
mylib.c

<>common_logging.h(70): error C4013: ‘WPP_CALL_mylib_c70’ undefined; assuming extern returning int

xxxxx@hotmail.com wrote:

I need some help fixing a WPP compiler error - specifically that the trace function is not found. I’ve been following every bit of guidance I can find (osr, MDSN, NTDev) and can’t seem to find the secret sauce. While I’ve been able get basic WPP working in a kernel driver, I’m now trying to add it to a static kernel library. I was trying to follow this MSDN page:

But that didn’t seem to help with the compiler error. Also, for the project I’m working on, it’s a large codebase and trying to include the libraries GUID and other info in a shared spot like the 2nd part of that MSDN page somewhat implies gets complicated.

I believe you are using the WPP macro incorrectly. Remember that WPP is
a preprocessor – it scans your code looking for instances of the WPP
macro by name (TraceEvents in your case), and pulls the strings from
that macro to store in the .tmh file.

In your case, you only have one call to the WPP macro, inside your
inline debug handler, and it doesn’t have any strings. The WPP
preprocessor doesn’t know what MY_vprintk is – it’s just another function.

If you want to use WPP, and you want your debug handler to be called
MY_vprintk, then that needs to be your WPP macro, not TraceEvents. In
your case, MY_vprintk has the same signature as TraceEvents, so you
could just change your RUN_WPP macro to specify
-func:MY_vprintk(LEVEL,MSG,…) .


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Oh, and one thing I forgot to add:
Per suggestions on other WPP threads, I’ve been reading the tmh file. I diff’ed my libraries tmh file vs one in my driver (that has wpp working), I notice large swaths of code missing. Most of it, as expected is all the functionality to actually stuff (WPP_PUBLIC_ANNOT_, WPP_RECORDER_ and, of course WPP_CALL_ ).

-Eric

> If you want to use WPP, and you want your debug handler to be called
MY_vprintk, then that needs to be your WPP macro, not TraceEvents. In
your case, MY_vprintk has the same signature as TraceEvents, so you
could just change your RUN_WPP macro to specify
-func:MY_vprintk(LEVEL,MSG,…) .

Tim -
Thanks. I’ll look into your suggestion. I do have one question to close my mental loop. Using the code above, I’d tweak my common_logging.h to prototype the My_vprintk so that it gets inline’d and then, per your suggestion, tweak my wpptrace.h file to define my wpp func as My_vprintk (as opposed to TraceEvents)?

So common_logging.h would change to:

INLINE void MY_vprintk(ULONG type, const char *fmt, va_list args);

and the wpptrace.h would change per your suggestion.

Is my mental understanding correct?

And as an aside, have you had experience with shared libraries and WPP per the referenced MSDN doc? Can I just leave the GUID as 0,0,… per its suggestion? It was unclear from that page (to me at least) if I’d need to call WPP_INIT_TRACING within the library’s entry or if it will magically appear via the driver that compiles the library in.

Thanks again for the suggestions!
-Eric

xxxxx@hotmail.com wrote:

Tim -
Thanks. I’ll look into your suggestion. I do have one question to close my mental loop. Using the code above, I’d tweak my common_logging.h to prototype the My_vprintk so that it gets inline’d and then, per your suggestion, tweak my wpptrace.h file to define my wpp func as My_vprintk (as opposed to TraceEvents)?

Actually, you would ELIMINATE your MY_vprintk completely (unless you
want to keep a version that works without WPP). The preprocessor will
replace all instance of MY_vprintk with calls into WPP – your binary
will not have any trace of a function called MY_vprintk.

So common_logging.h would change to:

INLINE void MY_vprintk(ULONG type, const char *fmt, va_list args);

I think your “common_logging.h” becomes unnecessary.

And as an aside, have you had experience with shared libraries and WPP per the referenced MSDN doc? Can I just leave the GUID as 0,0,… per its suggestion? It was unclear from that page (to me at least) if I’d need to call WPP_INIT_TRACING within the library’s entry or if it will magically appear via the driver that compiles the library in.

The “ff546863” MSDN page talks about this. The library does not call
WPP_INIT_TRACING – that’s done by the driver. If the driver doesn’t
call it, then tracing will not be available.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Tim -
Thank you again. Your suggestions have helped. I do have one bit that I could use your further insight with regards to the preprocessor.

With My_vprintk being marked as ‘inline’, it gets essentially copied in on every location it’s called (yeah, there are more nuances to that). So in common_logging.h it now essentially needs an extern prototype since that actual definition is now in myLib.tmh. While, in mylib.c, I’ve included both common_logging.h and mylib.tmh (in that order), but I think, due to INLINE, it’s still not finding the my_vprintk definition. Given the wider codebase, I can’t change that specific expected behavior.

I’ve done 2 things:

  • I’ve pulled all the info from wpptrace.h into common_logging.h and changed the RUN_WPP settings appropriately.
  • If I prototype the my_vprintk function in common_logging.h the compiler doesn’t complain, but I’m concerned it’s not picking up the right items. I’m about to test, so this datapoint might be premature. Given your knowledge of the preprocessor I figured I’d ask in parallel.

I hope I’ve articulated my question and appreciate the assistance.
-Eric

xxxxx@hotmail.com wrote:

With My_vprintk being marked as ‘inline’, it gets essentially copied in on every location it’s called (yeah, there are more nuances to that). So in common_logging.h it now essentially needs an extern prototype since that actual definition is now in myLib.tmh. While, in mylib.c, I’ve included both common_logging.h and mylib.tmh (in that order), but I think, due to INLINE, it’s still not finding the my_vprintk definition. Given the wider codebase, I can’t change that specific expected behavior.

No, if you did what I suggested, then MY_vprintk is no longer a function
at all. It’s a macro. There’s nothing to extern. As long as you are
including “xxx.tmh” in the file “xxx.cpp”, then the macro will be
defined, and everyone will be happy.

And please note that you spelled it “My_vprintk” and “my_vprintk” in
that paragraph, but your original code had “MY_vprintk”. It matters.

I’ve done 2 things:

  • I’ve pulled all the info from wpptrace.h into common_logging.h and changed the RUN_WPP settings appropriately.
  • If I prototype the my_vprintk function in common_logging.h the compiler doesn’t complain, but I’m concerned it’s not picking up the right items. I’m about to test, so this datapoint might be premature. Given your knowledge of the preprocessor I figured I’d ask in parallel.

If you have to prototype the function, then you haven’t actually changed
the WPP setup.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Tim -
Thanks again. Yeah, I realize that it matters (My_vprintk vs my_vprintk). I verified I had the same case. When I remove the prototype and depend on the tmh file, no dice. I’m back to getting:
error C4013: ‘my_vprintk’ undefined; assuming extern returning int

I’ll keep digging through. It’s extremely frustrating that WPP is this fragile.

And again, thank you for the insight!
-Eric

xxxxx@hotmail.com wrote:

Thanks again. Yeah, I realize that it matters (My_vprintk vs my_vprintk). I verified I had the same case. When I remove the prototype and depend on the tmh file, no dice. I’m back to getting:
error C4013: ‘my_vprintk’ undefined; assuming extern returning int

I’ll keep digging through. It’s extremely frustrating that WPP is this fragile.

It’s not, really – it’s just a matter of getting the recipe right.
After that, it’s easy.

Do you want to send me your project through email? I can take a quick look.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Tim -
Thanks again. I’m unable to share the project as it’s both quite large and proprietary. The code I posted here is a collection of sanitized snippets. But thank you for the offer.

I’m still working through a few headaches and would appreciate any additional insight you could spare. To expand on the original code, the overall layout is a tad more complex (big surprise…). I’ll try to add what I hope are relevant details.

There are 3 .c files in this library:

  • mylib.c
  • myqueues.c
  • myutils.c

they include common_logging.h and wpptrace.h per the original code I posted. Each includes wpptrace.h before both common_logging and the specific .tmh file for each file. That is, in myqueues.c I include wpptrace.h, then myqueues.tmh then common_logging.h.

That was the original code. Per your suggestions I tweaked the internals of common_logging to pull in all the wpptrace.h details. I’ve not been able to get the compiler happy in finding the trace macro. I believe it has to do with the trace function (my_vprintk) being inline and the include ordering of things. With all the macro items into common_logging.h and setting the WPP preprocessor to scan it, I can obviously put that first in my include order ( rather, nuke the #include “wpptrace.h” and replace with #include “common_logging.h”), but I still get the trace macro (my_vprintk) as undefined for each of the .c files. I’m probably looking at this all wrong and appreciate its’ a preprocessor that scans the code but my current thought (and I’m probably wrong) is that since it runs before the c preprocessor which does the inline substitution, life is not happy.

I was going to include the .tmh file into the common_logging.h file, but the issue there is that then how do I dynamically point to a different .tmh file depending on the .c file.

Finally, another wrinkle that I alluded to with regards to static libraries: Going on the assumption this finally gets compiling, I then have the headache of multiple WPP_CONTROL_GUID structures and the associated items when I pull this into a kernel driver. I know because when I prototyped the trace macro (incorrectly) and compiled my driver that includes this library I got the phonebook of WPP_macroX already defined. The driver has its own WPP_CONTROL_GUID structure and equivalent wpptrace.h file that has been working.

I then headed down the route of 1 .h file that has both control guids per the msdn article we’ve both referenced, but it still gets goofy.

Again, I appreciate the insight. If this project wasn’t already far along and some of these changes established, it’d probably be easier to bolt on wpp/etw support. I’ve got some latitude, but some bits can’t be moved about unfortunately.

Thanks.
-Eric

xxxxx@hotmail.com wrote:

There are 3 .c files in this library:

  • mylib.c
  • myqueues.c
  • myutils.c

they include common_logging.h and wpptrace.h per the original code I posted. Each includes wpptrace.h before both common_logging and the specific .tmh file for each file. That is, in myqueues.c I include wpptrace.h, then myqueues.tmh then common_logging.h.

What purpose is your common_logging.h serving now? If you have made
my_vprintk your WPP trace macro, then the macro will be defined in the
.tmh files. YOU don’t define it.

You should have your wpptrace.h (which can be included in some other
master include file), followed in the individual .tmh files (which
cannot be coalesced).

That was the original code. Per your suggestions I tweaked the internals of common_logging to pull in all the wpptrace.h details. I’ve not been able to get the compiler happy in finding the trace macro. I believe it has to do with the trace function (my_vprintk) being inline and the include ordering of things.

Are you using WDK 7 or WDK 8? In WDK 7, you specified the macro using
the -func parameter in the RUN_WPP macro in your “sources” file. When
you do that, “my_vprintk” will be defined as a macro in the .tmh files,
and the calls will be replaced by some annotations and a call to
WPP_SF_. It can be instructive to look at the .i preprocessed files to
see what WPP has done to your code.

With all the macro items into common_logging.h and setting the WPP preprocessor to scan it, I can obviously put that first in my include order ( rather, nuke the #include “wpptrace.h” and replace with #include “common_logging.h”), but I still get the trace macro (my_vprintk) as undefined for each of the .c files. I’m probably looking at this all wrong and appreciate its’ a preprocessor that scans the code but my current thought (and I’m probably wrong) is that since it runs before the c preprocessor which does the inline substitution, life is not happy.

“inline” is not the right word. These are macro substitutions done by
the preprocessor. “inline” is done by later compiler passes and results
in real code being generated.

I was going to include the .tmh file into the common_logging.h file, but the issue there is that then how do I dynamically point to a different .tmh file depending on the .c file.

You can’t. You must include xxx.tmh in xxx.cpp.

Finally, another wrinkle that I alluded to with regards to static libraries: Going on the assumption this finally gets compiling, I then have the headache of multiple WPP_CONTROL_GUID structures and the associated items when I pull this into a kernel driver. I know because when I prototyped the trace macro (incorrectly) and compiled my driver that includes this library I got the phonebook of WPP_macroX already defined. The driver has its own WPP_CONTROL_GUID structure and equivalent wpptrace.h file that has been working.

I then headed down the route of 1 .h file that has both control guids per the msdn article we’ve both referenced, but it still gets goofy.

Yes, that’s the path. You need to report both GUIDs in the driver.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.