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")
#define DRIVERNAME FILTERNAME TEXT(".sys")
#define DRIVERNAME_DBGOUT FILTERNAME TEXT(": ")
#if DBG
#define DBG_TRACE_VERBOSE 0
#define DBG_TRACE_MINIMAL 1
#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((DRIVERNAME_DBGOUT)); \
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 (
DriverObject,
sizeof (DEVICE_EXTENSION),
&DriverName,
FILE_DEVICE_UNKNOWN,
DeviceType,
FALSE,
&DeviceObject);
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
So the next part of the little Q&A series on...well, QA;
3.) What security considerations do you take into account when designing and developing a kernel mode driver?
Validate user mode pointers and check the buffers to and from user mode. ;)
That was a little inside joke for the person who asked the question. Seriously security and kernel mode are almost at odds with one another when you really think about it. Kernel mode is implied to be a trustworthy environment so anybody that's down there with you is expected to behave like a good citizen. And anybody that can talk to you from the nether region (user mode) should be treated as an enemy. As security for kernel mode drivers is something of a loaded question in the end, the best advice I can offer here is; Anything that can be accessed by anybody outside your driver should have some form of defense built in to it. I.e. if you have an external callback, think about how that that could be used as an attack entry point. Could somebody float recursive calls to that callback and cause a stack over flow?
That's about the only weird tip I can give other than using what's published here and here as a the base.
And finally, my apologize to Bob, I guess I stole some of his thunder. :)
*Currently playing - Rush Faithless
I got a few interesting questions recently, which was helpful because I was having a tough time finding good material for you guys and gals of late. That's the problem with working on the next Operating System, I can't tell you about all the cool stuff yet. *g*
So here's today's question (in two parts);
1.) Do you use a single system to code, compile, verify and test a kernel driver?
Nope, pretty much, kinda sorta and nope. We're actually pretty wide open as far as coding environments around here. Across this team alone you'll find Visual Studio, gVIM, Slick Edit and probably 2 or 3 other IDEs in use (I've actually even seen notepad used once). The only coding requirements are really based around formatting, naming convention(s) and the like. Which is really fun when you get somebody who crafts a build script that goes out and checks for tabs rather than spaces in source code. ;)
Our compiler is a different story, pretty much everything we build is done so in very much the same environment shipped in the WDK. Yes there are some caveats, but that's only because I can't speak for every team and some of our internal process isn't really meant for general consumption. But our compiler is linked to our source control environment which makes wrapping a full one stop shopping coding / compiling / source control environment more of a challenge. There are a lot of great tools internally to get most of it done depending on your IDE of choice. Personally (if you haven't figured it out yet), I use Visual Studio as my source editor, a couple of internal tools to manage source control from within Visual Studio and then build in that external razzle environment. Like I said before, I'm still not prone to giving up some of my past. :)
Verification is another tricky question (somewhat open ended really). If we're talking about compile time code verification, then yes we do have a somewhat unified system in place. We use the same tools we ship in the WDK, PREfast and Static Driver Verifier. Our internal build system actually runs PREfast for us as a post build process.
Finally our testing is a real Pandora's Box type question. We do use the same tools we ship externally, Application Verifier, Driver Verifier and WDF Verifier, but we also develop other tools internally to cover scenarios we just can't reach using those tools. Here again, it's not a one stop shopping system across the Windows teams, but for those of you going to the DDC, Bob, Shyamal and Wei will be giving a shared secrets talk which will cover some more of what we do internally.
2.) How do you use virtualization software in the test and dev process?
This is my favorite one, I've been on a big push around here to get more people using virtual environments. Personally I've been using Virtual PC since 2005, I did some test work for them on that release, so I got in on the ground floor somewhat. Anyway, I use VMs to extend test coverage primarily on down level OSes. I keep "clean" installations of Windows XP SP1, SP2, SP3, Server 2003, Vista and Vista SP1 and some variants of those on my system. A lot more people are starting to use HyperV and Virtual PC around here, but given some of the limitations of those environments (e.g. inability to map USB hardware) and other little nuisances we can't use them for a full end to end test beds, this is a little more intrusive on the kernel side where you may require a piece of hardware. I know there are other virtual environments out there that do allow these features, but we're kind of prone to using our own products. :)
*Currently playing - King's X Alone
Seriously, Sara rocks for doing this.
Sara Ford's WebLog : Microsoft Visual Studio Tips Book Helps Hurricane Katrina Survivors Rebuild Lives
I know quite a few of you use Visual Studio, and if nothing else, you should keep an eye on her blog for tips and tricks. Sure, sure, not all of them pertain to us crazy driver developers, but there are those tidbits we can modify for our benefit. And as we are now floating precariously close to "normal" developers with UMDF, we may find more and more of her Tips of the Day useful. :)
And personally, I'm buying two copies of that book and adding her to the list of reasons I tell people why I work for Microsoft.
Back with something more chewy, salty and sweet driver stuff in a week or so.
From the, "it's funny because it happened to me" file, I did it again. While debugging a recent issue in the test lab, I had pushed the root cause out as an attempt to free an invalid handle. I cracked open the code to find the line and looked at it in passing, "yup, simple logic error, if ((deviceHandle != NULL) && (deviceHandle != INVALID_HANDLE_VALUE))" What I saw was really not what I thought I saw. Yup, I saw the reverse of this where it was checking if they were equal so I suggested the fix that state the && be changed to || and it should be if (! (deviceHandle == NULL) || (deviceHandle == INVALID_HANDLE_VALUE)). Then Bob pointed out the obvious. :)
On the testing side, we had a meeting recently to discuss some new test tools and there the continual theme Bob started and I picked up on came to pass; Testers should be the most paranoid people in the world. Over the years I've had my concepts of design and coding altered greatly. I went from the simple error handling should the odd API / DDI fail to the belief that they will always fail. As a result, my error handling routines have become exponentially more robust and adaptive. So during this meeting Bob and I were always thinking negative, "how are you going to handle when this fails, because it will fail. Believe it or not, at some point, it will fail." Yessir, I'm just a negative guy.
And finally, so long and thanks for all the fish Bill. Yes, I'm feeling terribly melancholy today.
*Currently playing - Sloan All Used Up
I'm on a pet peeve kick lately and another one of my big ones is WaitFor<Single | Multiple>Object with an INFINITE time out period.
You do realize it's INFINITE right? And while you can mitigate the risk of actually spinning forever on an object, there's really never 100% certainty that it will get signaled. As Bob pointed out, we SDETs should be and quite often are, fastidious and paranoid people and my paranoia rate increases when I see an INFINITE flag in test code.
Case in point;
retVal = WaitForMultipleObjects(
HandleCount,
Handles,
FALSE,
INFINITE);
Seems harmless enough right? You've got some in your own code, right? And if in testing you find this blurb hanging, the normal behavior is to fix where that handle is not being triggered, right?
Not so fast, in this particular case, the handles are events, and these events are packaged up and sent to a test driver which then signals the events (there's a case condition which verifies the signaled event is actually the one we want).
All works well until you get a malicious tester like me who runs around and does something to intentionally block the driver from successfully loading. And BOOM goes your test, or in some cases your driver. This leads to emails from our lab engineers generally titled; <test> is hanging on <os | arch | build> in lab. And invariably, it's because there's a condition which wasn't expected causing this stall.
So, change the timer!
retVal = WaitForMultipleObjects(
HandleCount,
Handles,
FALSE,
120000);
I know what some of you are going to say, "but I can't guarantee the operation will be completed in two minutes". No you can't, but somewhere between 0 and INFINITE - 1 it should be done, and if it isn't, it's a bug. Maybe not in your code, but in somebody's code. I know there are corner conditions where INFINITE is a valid flag, and I'm fine with that, but I have a tough time believing there are that many. :)
m_Grumpy_Old_Man_Mode->Off();
*Currently playing - Dream Theater Status Seeker
Things are crazy around here, but I figured I couldn't leave all my loyal reader in the dark as to my whereabouts.
As you remember we had something of a hiccup with the coinstallers, and that just seemed to be followed by a flood of work items, bugs and such for the Windows 7 project.
I swear as soon as I can tell you anything about what we're doing I will and as soon as I can post something more interesting that the following, I will. :)
So, as I get older I find that my list of pet peeves or things I get all cranky about grows, I'd say exponentially, but it's more in the leaping incremental stage right now. The latest addition to my peeve list follows the onslaught of multicore CPUs and the staggering amount of confusing information out there that poor Average Joe / Josephine has to sift through. Of all that information, for some reason the one that really honks me is when people say, "you won't see any difference because <insert application name here> is single threaded..." *faint*
Wow, I didn't realize we were still in 1982! What usually confuses the purveyors of such statements is when I start explaining the Operating System thread scheduler with sprinkles of how the thread quantums and queues work. Then for some frosting, I follow this with some diatribe on how <insert application name here> is actually multithreaded, but just "weighted" towards a working single thread. Add a little demonstration on how many threads are actually running around in that process space and a touch of how <insert application name here> really just has some bad resource contention, synchronization and clunky included libraries that cause those child threads to just twiddle their thumbs until daddy thread tells them it's okay to do something.
Yes, I realize it's a stupid semantics issue, but I don't care. I'm old and cranky and the Sharks are playing horrible hockey and so are my beloved Canadiens. :(
Oh and get off my lawn. ;)
*Currently playing - Pink Floyd One Slip
They're bbbbbbbbaaaaaaaaaaacccccccccckkkkkkkkkkkkkkkkkkk. After some incantations, Ouija boarding and me dancing around a build server with a spindle of DVDs on my head and sacrificing a serial port mouse, the fixed 1.7 coinstallers are available.
Sorry for the delay and the initial issue... The 18002 build of the WDK should be out shortly (about a month...or so) which will have all the WDF redistributables back in their original homes.
Thanks for your support.
*Currently playing - The Who Won't get fooled again
I love sharing simple mistakes for some reason. I think it's because we're all prone to searching for how to do something simple, like handle METHOD_NEITHER requests in our KMDF driver, and only read the documentation on how to initialize that ability, but not remembering all the little rules that govern the surrounding APIs. Oft leaving us with a series of bug checks, driver load failures or head scratching "huh" scenarios.
This not so good -
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (!NT_SUCCESS(status))
{
KdPrint( ("WdfDeviceCreate failed with status code 0x%x\n", status));
return status;
}
<...some other code went here...>
WdfDeviceInitSetIoInCallerContextCallback (
DeviceInit,
SomeEvtInContextCallBack);
This good -
WdfDeviceInitSetIoInCallerContextCallback (
DeviceInit,
SomeEvtInContextCallBack);
status = WdfDeviceCreate(&DeviceInit, &deviceAttributes, &device);
if (!NT_SUCCESS(status))
{
KdPrint( ("WdfDeviceCreate failed with status code 0x%x\n", status));
return status;
}
See, at some point in history, I had paged out of my grey matter memory module that tidbit in the MSDN documentation for WdfDeviceCreate which says; After the driver calls WdfDeviceCreate, it can no longer access the WDFDEVICE_INIT structure.
Boy oh boy you sure can't access it afterwards, that bad boy is a glorious, awe inspiring, blue screening NULL after WdfDeviceCreate is through!
First apologies for the disappearing act. I would like to say I was trapped under a fallen 10- year collection of Dobb's C++ Journals and couldn't get free, but it'd be a lie. A good lie, but a lie none the less.
I would also like to apologize for not providing you with a bit of April Foolery, but I'm suffering a lack of 'teh funnee' right now.
The Intellisense project is on the very, very back burner right now. Consider it a crock pot / slow cooker project. But I will actually strive to release it as a tool for you all and as such I'm going to have to go through a lot more hoops. It was going to simply be a little script, but I figured at that point I'd just make a nice Visual Studio plug in and see about distributing it.
Lastly, Jonathan Coulton is my new Hero. I just picked up full Thing A Week collection after having bits and pieces of them here and there. He so easily transitions from one musical style to another and has some great melodic hooks in his songs. Add to that his fascination with monkeys, robots and evil geniuses and he speaks to me on many levels.
I'll get back to some more beefy flavored posting soon.
*Currently playing - Jonathan Coulton Skullcrusher Mountain (Demo)
Because this one goes to eleven!
Good stuff is bound to come from a man who enjoys 80's metal, dark matter, kernel mode and debugging. All the cool kids are in to those things.
*Currently playing - Top Gear 10x01
As people who write drivers are something of a rare breed, I know we probably share a lot of common readers between our Driver blogs and via the OSR boards, but just in case the three or four of you who read my blog haven't seen this, I wanted to make sure it got out there.
We had a bit of a stumble with the 1.7 coinstaller package that went out with the WDK recently.
We're pretty close to being done with a fix as Bob said, so fret not. I'd like to thank those who pestered me about the issue for helping narrow it down, and also those who pestered the external boards and email alias.
Now, if we could all join hands around the camp fire and sing "row, row, row your boat" to complete this little moment of community unity.
Still working on the Visual Studio stuff, but those of you with free time are more than welcome to skip ahead.
*Currently playing - Not a single thing...
Work and home work have prevented me from completing the 2nd part of the Intellisense Musical. I had asked my wife if I could have Saturday to work on it, and of course after the trip to Lowes for paint supplies and spending hours with her setting things up and answering questions, it was time for me to make dinner.
I won't promise that I'll have it ready any time soon, but that I am working on it as I get the chance to. The primary stumbling block from me just releasing what I have built internally is, well, it's got a lot of internal stuff in there and I'll guarantee you that won't go over well with superiors and the like. So I'm trying to make a generic enough version that you can all use (should you choose to) but something that also won't result in a cavalcade of "HELP" mails. :)
So, in deference to that, I've been pinged a couple of times and asked how to make it work when you're using a MAKE project file. And in that case it's even easier than a C/C++ project.
*Currently playing - Not a single thing...

It's actually the 15Mbps / 15Mbps package, but I can't find any test servers that will actually push the 15Mbps up limit, so it may not be a "pure" 15Mbps up, but man is it close.
Next question is "why"? It does make it easier to work from home and also access my home machine from work...and stream music from it...and host really big GTR2 multiplayer races...you know, all the good stuff.
Don't know how much of the second act of the Musical I'll have for you this weekend, I may have to publish it in two parts.
*Currently playing - Spocks' Beard, The Planet's Hum