How to find the droids you might be looking for

Most of you at the DDC will notice that I'm not at the DDC.  I'm guessing that makes me one of the "juniors" that Bob was referring to. 😉

4.)  How do you build debugging techniques into your driver?  Ie, DbgBreakPoint, ASSERT, etc - which is best in various situations? 

This is one of those personal preference type questions.  For the sake of lineal chatter, I'll just use ASSERT in my commentary.  But when I say "ASSERT" you can make the determination on which one to use given your own working methods.

So that preamble aside, it's on with the show.  I like to have staged text output via a debug trace level as a first tier technique.  Something along these lines;

#define FILTERNAME        TEXT("mydriver")

#if DBG
#define DBG_WARNING 2
#define DBG_ERROR 3
#define DBG_FATAL 4

extern unsigned char DebugLevel;

#define DBG_PRINT( l, _x_ ) \
if( (l) >= DebugLevel ) \
{ \
KdPrint( _x_ ); \
#else // DBG

#define DBG_PRINT( l,_x_ )

That allows me to compartmentalize failures and thusly allows me to build handlers for each.  The question of what to do in each of those situations is another personal preference. As you can see from the error levels above, I follow patterns of building in handlers that fall in to - terminal, really annoying, nuisance, does it work and assuage.

I'll ASSERT more frequently for the nuisance issues, but also for the terminal, and some of the really annoying issues.  Nuisance issues such as that HANDLE disappeared while I was using it, better ASSERT. This is usually when I find that the other thread using that handle wasn't synch'd properly...laugh all you want, you've done it too. 🙂

Using those levels as a guide, I craft an appropriate follow up action.  But here I let the driver tell me what to do and it does that based on its end usage.  For example;

  Status = IoCreateDevice (
if (!NT_SUCCESS (Status))
DBG_PRINT (DBG_FATAL, TEXT(("Failed IoCreateDevice: 0x%x\n", Status)));
ASSERT (DeviceObject);
return Status;

Would I use that ASSERT in a test driver?  Not likely, all too often our test cases attempt to invoke failures in just those sort of areas and we run chk (debug, no_opt, etc.) builds in our labs frequently. So that ASSERT would be triggered constantly.  You could also argue that in a shipping driver you would hit it in a lab under low resource simulation testing, but unless you plan on running low resource simulation 24/7, the nuisance factor is diminished greatly.  But having the debug print there is a means for you to see if something happened in a retail / fre build of the driver.  You can also wrap Windows logging methods with the same mentality such as WPP and ETW.

All of the above starts with the driver though.  The more complex the driver, the more robust the debugging logic, so some of my internal test drivers have one or two ASSERT calls and maybe a couple of lines of debug print statements.  So if you peek at the 1394vdev hybrid sample you can get a better idea of my patterning.  The lack of ASSERT calls was a design choice based on the pattern established by the previous versions of that sample driver.

So for those of you at the DDC, enjoy the DDC!  If you feel like making Peter laugh, ask him why I'm such a dork.

*Currently playing - Gravity Eyelids Porcupine Tree

Comments (3)
  1. strik says:


    I want to comment on three things:

    1. In your DEBUG_PRINT() macro, you are using two separate KdPrint() calls. I was bitten by this in the past, as nothing guarantees these two KdPrint calls are executed one after the other. Thus, you might end up with the prefix of your driver and some completely different output, which is completely unrelated to your driver. Yes, in most cases, you can live with this limitation, but you have to know about it.

    I have decided to do it differently now: In my debugging macros, a debugging output line is built up in a buffer and written out with one call to KdPrint(). This way, I do not have this limitation.

    1. Personally, I use a self-defined macro DBG_ASSERT() which combines your DBG_PRINT() and ASSERT() functionality in one. Additionally, DBG_ASSERT() only breakpoints if some bit flag is set. That way, I can decide myself if I want to have that break, or not.
    2. Personally, I prefer debug flags over debug levels. This allows for better extensibility, and more fine-grained control over what I want to see (cf. also the bit flag in 2 which controls if an DBG_ASSERT() should include a breakpoint or not).



  2. says:

    I have moved over to using a ULONG stacked flag value in some of my UMDF Drivers and moreso in test code actually.  It cuts down on generating really huge log files.

    For the User Mode work I am also using a macro’d function like you describe but in kerenl mode I still tend to just use ASSERTMSG or ASSERT with my own output routine.  Old habits are hard to break. 🙂

    Good stuff, never seen the KdPrint issue before, I’ll keep that in mind! Thanks.

  3. bruteforce says:

    Basically, I have my own custom-tailored ASSERT that can be present in both release and debug build, will always generate a debug message with the failing source file and line number and only do a debug break if a registry setting is set to 1. This way I can run debug builds on clients’ machines without BSODing them when an ASSERT failes.

    You can check out a related post I made at

    I have also faced the split KdPrint issue in the past and it can be a major annoyance if (a) there is a lot of debug output (b) user is collecting output with a tool like DbgView and forgets the "Force Newlines" menu checked.

    Another thing that might be of interest to you guys, is the VA_ARGS ‘macro’ which allows one to build ‘variadic’ macros!!!

    #define DBGPREFIX "[MyRegardsToHlias.SYS]: "

    #define FGTRACE(_fmt, …) DbgPrint( DBGPREFIX _fmt, VA_ARGS)

    This way I don’t have to write double parenteses in each call to FGTRACE! I don’t know about you, but these ‘double paren’ macros always felt ugly.

    The above example expects that _fmt will always be a const character string, not a char * pointer variable for example, so that the const string concatenation will work. A minor issue I would say 🙂

    You could even make the DebugPrefix kind of dynamic as shown below:

    char g_DBGPREFIX[80];

    #define FGTRACE(_fmt, …) DbgPrint( "%s" _fmt, g_DBGPREFIX, VA_ARGS)

    It would be an interesting problem how to update the prefix variable string without breaking a debug output call that is in progress and without penalizing the performance of a multitude of output calls so that you can update the buffer safely every now and then.

    Cheers guys!

    Dimitris Staikos

Comments are closed.

Skip to main content