The NT Insider PDF
The NT Insider PDF
The NT Insider
®
server with a root share that contains Aside from the potential aliasing issues What‘s a Bug Bash Contest? Well,
multiple links. To clients, the links that this architecture introduces, which aren‘t you going to be glad you asked.
appear as regular folders under the root exist in other scenarios anyway, this It‘s easy: you find and report bugs in
share, however they can point to shares seems like the sort of thing that should the Windows Driver Kit (WDK).
either on the same server system or on be handled transparently to the client. That‘s it. That‘s all you have to do.
any other computer in the organization. Unfortunately, due to some architectural
and design decisions made in the From there, the bugs will be triaged
For example, you may access a share implementation of XP‘s DFS client and reported directly to the Microsoft
from your client machine via the path support, dealing with DFS can quickly WDK team—so at a bare minimum,
\\DfsServer\DfsRoot\Documents. This become a hornet‘s nest at the bottom of you are helping to get bugs fixed in
could then end up being a link to an a rat hole. In addition, the Filter the WDK.
e n t i r e l y d i f fe r e n t s e r v e r , s a y Manager mini-filter abstraction attempts
\\FooServer\Share. Thus, ignoring the to hide some of the implementation But wait, there‘s more! It wouldn‘t be
other features and options that DFS details and in the process complicates any fun if we didn‘t run a contest,
provides, DFS is effectively a name things even more. where every valid bug submission
aliasing technology that allows an earned a prize, and where all valid
administrator to redirect client requests Before we begin, we should note that bug submissions were judged against
from one machine to another. substantial changes have been made to one another for eligibility to win one
this space in Vista and thus the of several really worthwhile grand
Frustratingly, over the years we‘ve discussions in this article only apply to prizes (if you will).
regularly run into nagging issues with O/S releases prior to Vista unless noted
DFS‘ interaction with the file system otherwise. Really, in the end, if you read this
filter drivers that we have written. To (Continued on page 18) publication, the WDK is required for
some portion of your job function, and
thus improving upon it can only help
you.
The NT Insider Goes Digital– Good?
And hey, you could use a iPod touch,
You are reading the first electronic version of The NT Insider! We’re an Visual Studio Ultimate with
saving trees, a gazillion dollars in print and mail services and getting the MSDN subscription, or a netbook to
play around with, right?
issue into your hands at least 2-3 weeks earlier than the print edition.
For more, see the center page spread
Does this medium work for you? Why or why not? What can we do better? on pages 16-17.
The NT Insider™
Published by
Inside This Issue:
OSR Open Systems Resources, Inc.
105 Route 101A, Suite 19
Amherst, New Hampshire USA 03031
(v) +1.603.595.6500 (f) +1.603.595.6503
Writing Filters is Hard Work: Undocumented DFS & RDR Interactions 1
http://www.osr.com
Consulting Partners
W. Anthony Mason
Seeking International Partners & Contacts 3
Peter G. Viscarola
Executive Editor
Daniel D. Root Peter Pontificates: Pros & Cons of Agile SW Development Methodology 4
Contributing Editors
Mark J. Cariddi
Scott J. Noone
OSR Associate Staff Getting Better: Virtual Storport Tweaks 6
Consultant At Large
Hector J. Rodriguez
Send Stuff To Us:
email: [email protected] The Basics of Debugger Extensions: Short Term Effort, Long Term Gain 8
We really try very hard to be sure that the information we Analyst’s Perspective: Debug Smarter 30
publish in The NT Insider is accurate. Sometimes we may
screw up. We‘ll appreciate it if you call this to our
attention, if you do it gently.
OSR Seminar Schedule 32
OSR expressly disclaims any warranty for the material
presented herein. This material is presented ―as is‖ without
warranty of any kind, either expressed or implied,
including, without limitation, the implied warranties of
merchantability or fitness for a particular purpose. The
entire risk arising from the use of this material remains with
you. OSR‘s entire liability and your exclusive remedy shall
OSR USB FX2 Learning Kit
not exceed the price paid for this material. In no event shall
OSR or its suppliers be liable for any damages whatsoever.
Don’t forget, the popular OSR USB FX2 Learning Kit is available in the Store
It is the official policy of OSR Open Systems Resources,
Inc. to safeguard and protect as its own, the confidential at www.osronline.com.
and proprietary information of its clients, partners, and
others. OSR will not knowingly divulge trade secret or
proprietary information of any party without prior written The board design is based on the well-known Cypress Semiconductor USB FX2
permission. All information contained in The NT Insider chipset and is ideal for learning how to write Windows device drivers in general
has been learned or deduced from public sources...often
using a lot of sweat and sometimes even a good deal of (and USB specifically of course!). Even better, grab the sample WDF driver for
ingenuity.
this board, available in the Windows Driver Kit (WDK).
OSR is fortunate to have customer and partner relations that
include many of the world‘s leading high-tech organiza-
tions. As a result, OSR may have a material connection
with organizations whose products or services are dis-
cussed, reviewed, or endorsed in The NT Insider. The NT Insider—Digital Edition
Neither OSR nor The NT Insider is in any way endorsed by If you’re a new to The NT Insider (as in, the link to this issue was
Microsoft Corporation. And we like it that way, thank you
very much.
forwarded to you), you can subscribe at
http://www.osronline.com/custom.cfm?name=login_joinok.cfm.
Page 3
Regions of Interest
While this is not a partner search that is limited to any specific
country or region, of particular interest are partners in India,
Greater China and Eastern Europe. If you believe your
organization would be a valuable partner, we do want to hear
from you regardless of the market you serve.
What OSR Students Say
What Constitutes “Partner” Potential?
In order to align with international partners that will be a Don’t take our word for it. This is what students say about
value-add and credit to the OSR brand, we are most interested our seminars:
in discussing proposals with companies and individuals with
the following: "The word I would use to describe the class is - great.
I will recommend anyone starting out with device
Technical expertise in Windows internals and kernel drivers to attend this seminar. For me, being a
mode software development and testing
developer who has never written a driver for Windows,
it was a great start."
Experience in providing services or solutions that
involve significant Windows kernel-mode
components ―This was my first OSR seminar and probably won't be
my last. I have always heard good things about them
Clear understanding of the markets served and totally agree. The week was enjoyable, entertaining,
informative and I left Munich feeling 100% more
A history of success in business, accompanied by a confident in tackling more debugging issues.―
long list of happy customers
"[Instructor] is a very good tutor. He presents the
Ownership and staff that demonstrate integrity, moral material with a (loud and) clear voice, often repeating
character and strong sense of business ethics stuff so one won't miss a thing. His long experience in
the field shines through. When he got a question he
Effective communication skills both with partners could often refer to how it was designed and
and customers implemented in the Windows source code itself, and
thus bring a very clear and understandable answer."
Page 4
Peter Pontificates:
Pros & Cons of Agile SW Development Methodology
I know that you, dear reader, have come to expect a certain
level of sophistication and journalistic integrity when it
comes to my monthly pontifications: A gloss of civility, a
A maniacal emphasis on ―just getting something
working‖ as opposed to thinking something through,
designing how it should work, and getting something
certain level of refinement, and an even airing of all sides of working correctly.
an issue. Thus, on those very rare occasions when I have a
personal opinion to present, I feel the need to make my Requiring detailed estimates of how long it‘s going to
viewpoint on the issue under discussion extremely clear. And take to implement some piece of functionality.
so it is for today‘s topic: Agile software development
methodology. An insane reliance on – and limitation to
implementing features for – user stories (AKA
Let me start, then, by clearly, dispassionately, and logically scenarios), which yield fragmented feature sets as
describing my point of view: opposed to designing for well-reasoned
comprehensive functionality.
Agile software development methodology sucks ass. Big
time. Totally. 100%. Entirely. For the life of me, I do not understand why I would ever want
to write code for a complex facility before I have had the
Gosh, I hope I‘ve stated that clearly enough. chance to design that facility and consider how it will interact
with everything else in its environment. For sure, managers
Let me explain further: I have never, not once, not ever, like it. They think designing things is a waste of time
participated in, seen, or even heard of a project in which Agile anyhow. This reminds me of clients who tell me to ―just write
works better than any well-organized alternative. Agile is some code‖ to do so-and-so. I work in kernel-mode, boys. I
without question the refuge of those with small minds, poor often work on complex pieces of larger systems. It doesn‘t
writing skills, and extremely high-levels of tolerance for make a lot of sense to just ―write some code‖ that‘s ―good
incomplete functionality that‘s written in a way that‘s ―good enough‖ – when you do that, you get code that doesn‘t handle
enough‖ to pass whatever tests happen to exist. corner cases, doesn‘t work or play well with others, and is
generally ill-conceived.
Just to make sure that you understand that I mean to slag the
entire, wide-ranging, genre that is Agile, I specifically mean to But Agile is all about ―just writing code.‖ System
include Agile permutations like Scrum. Basically, I mean to architecture? Design? In Agile, that‘s not in the plan. What‘s
include any software development methodology that relies on in the plan is ―code it up and see how it works‖ and ―you can
references to barnyard animals, where you stand-up to have fix it in the next sprint.‖ That‘s hosed. It‘s a waste of time.
meetings, or where you‘re supposed to account for what And while I‘m all about ―plan to write one to throw away‖, I
you‘re doing in units of, like, 45 minutes. sorta think that the word ―plan‖ is important there.
My major objections to Agile come from what I view as the
unmitigated stupidity of its three most important tenets: (Continued on page 5)
Getting Better
Virtual Storport Tweaks
S torport is a welcome relief to storage driver writers
wishing to develop a driver that exports a virtual device.
This article updates the Virtual Storport Miniport Driver that
emulate fully compliant SCSI devices, so in order to be
compliant the driver was changed to do this.
we developed in the earlier three issues of The NT Insider. No SRB DataTransferLength Update
The biggest violation that busTRACE found was the failure
Another Article? of the Storport Virtual Miniport to update the
Well to be honest, when we completed the last article we put DataTransferLength field in the SRB when completing a
the Virtual Storport Miniport Driver code away and expected command that involved a data transfer. While this isn‘t
not to have to revisit it for a long while. However, we were critical, in order to be properly compliant with the Microsoft‘s
contacted by Mike Berhan of busTRACE Technologies who SRB definition requirement, it must be filled in. Thus, for
used their busTRACE 9.0 product (www.bustrace.com) to see example, if you diff the old and new code you‘ll see that
how accurate our implementation was in emulating a SCSI OsrUserReadData and OsrUserWriteData now set the SRB
adapter/device. DataTransferLength field to the size of the data transfer.
So, How Did We Do? busTRACE also pointed out that when the driver failed a
Well, it seems that we missed some stuff. And while request, it was not setting the SRB‘s DataTransferLength
windows didn‘t complain, we‘d be doing the development filed to zero to indicate that no data was transferred.
community a disservice if we didn‘t clean the errors up and
indicate where those errors were. So the purpose of this article Validating the SRB DataTransferLength
is to tell you the errors that were found and how they were Another interesting busTRACE find was that the Storport
corrected. Of course, we‘ll replace the current download code Virtual Miniport Driver was not validating that the size of the
with the updated version. buffer described by the SRBs‘ DataTransferLength field
was large enough to hold the data requested. What this
So let‘s get into what Mike found, using busTRACE and means is that when the Storport Virtual Miniport gets a
busPROBE (which are terrific tools, by the way). request, for example a read or write, it needs to get the length
of the transfer described in the CDB and ensure that the SRB
Assumption of Pre-Zeroed Data Buffers DataTransferLength field is large enough. Failure to make
For SCSI operations other than read or write, Mike noted their this check would lead to crashes or data corruption since the
busPROBE product found that the Storport Virtual Miniport driver could attempt to access past the end of the allocated
driver did not pre-zero the data buffer which will contain the buffer.
output from the driver. Thus when the driver filled in the
return data, and fields that were not explicitly zeroed were
filled with random data.. (Continued on page 7)
While this may not seem like a big deal, remember that the
Storport Virtual Miniport Driver is trying to create and
Learn to Write KMDF Drivers
Why wouldn’t you? If you’ve got a new device you
need to support on Windows, you should be
considering the advantages of writing a KMDF
driver.
Extension DLL Entry Points The entire point of writing this DLL is to create your own
There is a single required entry point for extension DLLs,
debugger commands, and those will also be exports of your
DebugExtensionInitialize. This is where you will do all of
DLL. These commands or, extensions must appear in
your one time initialization for the extension DLL. A
the .DEF file associated with your DLL and the exports must
description of the entry point and the function prototype can
contain only lower case letters. The function prototype of an
be found in dbgeng.h:
extension command is as follows:
// Initialization routine. Called once when the
// extension DLL is loaded. Returns a version and // Every routine in an extension DLL has the
// returns flags detailing overall qualities of the // following prototype. The extension may be called
// extension DLL. A session may or may not be active // from multiple clients so it should not cache the
// at the time the DLL is loaded so initialization // client value between calls.
// routines should not expect to be able to query typedef HRESULT (CALLBACK* PDEBUG_EXTENSION_CALL)
// session information. (__in PDEBUG_CLIENT Client, __in_opt PCSTR
typedef HRESULT (CALLBACK* Args);
PDEBUG_EXTENSION_INITIALIZE)
(__out PULONG Version, __out PULONG Flags); You‘ll note that the function prototype indicates that a pointer
to a DEBUG_CLIENT structure is passed as the first
A simple example of a complete DebugExtensionInitialize is parameter to the extension command. This is actually the
as follows: client object whose QueryInterface method you will use to
extern "C"
gain access to the various client COM interfaces for target
HRESULT manipulation.
CALLBACK
DebugExtensionInitialize(PULONG Version, PULONG
Flags) Let‘s see a simple example command that uses the built in
{ expression evaluator to add two and two together and then
// displays the result. This would be the equivalent of typing ? 2
// We're version 1.0 of our extension DLL + 2 in the WinDBG command prompt.
//
*Version = DEBUG_EXTENSION_VERSION(1, 0);
HRESULT CALLBACK
// mycommand(PDEBUG_CLIENT4 Client, PCSTR args)
// Flags must be zero {
// PDEBUG_CONTROL debugControl;
*Flags = 0; HRESULT hr;
DEBUG_VALUE result;
//
// Done! UNREFERENCED_PARAMETER(args);
//
return S_OK; //
} // Let's do a couple of simple things. First
// thing to do is use the passed in client to
// access the debugger engine APIs.
//
// First, we'll get an IDebugControl so that we
There are two other entirely optional exports that you driver // can print messages.
can provide, DebugExtensionUninitialize and //
DebugExtensionNotify. DebugExtensionUninitialize can be hr = Client->QueryInterface
(__uuidof(IDebugControl),
used to undo anything that you might have done in (void **)&debugControl);
DebugExtensionInitialize:
if (hr != S_OK) {
return hr;
// Exit routine. Called once just before the }
// extension DLL is unloaded. As with
// initialization, a session may or may not be //
// active at the time of the call. // Now we can print.
typedef void (CALLBACK* //
PDEBUG_EXTENSION_UNINITIALIZE) debugControl->Output
(void); (DEBUG_OUTCTL_ALL_CLIENTS,
"mycommand running...\n");
If present, DbgEng calls the DebugExtensionNotify callback //
for session state changes: // Use the evaluator to evaluate an expression
(Continued on page 24)
Page 10
It turns out that ―data isolation‖ models are actually applicable Basic Model
to a variety of different uses including (but certainly not The basic model for an isolation driver is that it controls the
limited by): cache. This might seem to be more complicated than a
―typical‖ filter. In fact in our experience, any filter that finds
Encryption filters - they want to show a view of itself in the position of trying to manipulate the cache when it
the data that is different than the data contained in is owned by the underlying file system driver becomes much
the file. more complicated.
Compression filters - they want to show a view of
the data that is both different in size AND content
from the data contained in the underlying file. (Continued on page 11)
For more information, visit www.osr.com/fsd.html, or contact the OSR seminar department at [email protected].
Page 11
sitting on the network redirector, since the
Getting Away... redirector may change caching policy ―in flight‖).
For our sample, the native file will be the same size as the File System Development Kit
presented view.
Issues to Consider
Over the years we have seen a number of different filters that
How’s this for a problem: our customers feel little
try to: pressure to upgrade to new, functional releases of
our FSDK because it’s so stable. Now THAT’s a
Flush/purge the underlying file system‘s cache. problem we like to have.
Acquire locks (typically via the common header)
in hopes that the underlying file system uses that If your commercial, file system-based solution
specific locking model (a dangerous assumption, in
requires this same level of stability, why not consider
fact).
Uses undocumented fields in the underlying file
working with a codebase that has become the gold-
system‘s control blocks to determine caching standard for successful implementations of Windows
behavior (this is a problem for encryption filters file systems, and a company with the experience and
reputation to back it up.
Consider the advantages of working with OSR. If we can be of value-add to your project, we’ll tell you. If we
can’t, we’ll tell you that too. You deserve (and should demand) definitive expertise. You shouldn't pay for
inexperienced devs to attempt to develop your solution. What you need is fixed-price solutions with guaranteed
results. Contact the OSR Sales team at [email protected] to discuss your next project.
Page 13
/v – verbose output
Check This Out... /ac – to select the cross certificate
/sha1 – to indentify your code signing certificate‘s thumbprint
/t to embed a timestamp so the signature does not expire
(Continued from page 12)
Where to Go from Here?
again. Even if your driver package is correctly installed, the Hopefully, this short article has help clarify some of the more
driver will not be loaded unless it passes the KMCS check. confusing aspects of driver signing.
Attaching a kernel debugger disables the KMCS check.
Microsoft has published a thorough hands-on tutorial
The KMCS signature can be present either in your SYS file, describing the KMCS process on the WHDC web site (see [2]
or in your CAT file. Non-plug-and-play drivers usually won't further below). By carefully following the steps outlined in
have a CAT file, so the SYS file must be signed. If you sign this document, you‘re guaranteed to be able to successfully
the CAT file only, you cannot replace the SYS file (for sign your driver. Of course, this assumes you have the correct
example, during debugging). The CAT file contains a certificate (as described above)!
checksum of the matching SYS file. For this reason, I usually
sign both the CAT and the SYS. For drivers used during [1] http://www.microsoft.com/whdc/driver/install/drvsign/
system boot, Microsoft strongly suggests that you sign the crosscert.mspx
SYS file, because it can take too long to locate the [2] http://www.microsoft.com/whdc/driver/install/drvsign/
corresponding CAT file with the limited disk driver stack used kmcs-walkthrough.mspx
during boot.
When a thread attempts to acquire an EResource, it can Now that the basics have been explained, let‘s see the
specify whether or not it wants to wait if the specified functions available to us.
EResource is not immediately available. If it elects to wait,
the thread will be put into a wait state until such time as the
lock becomes available, otherwise the acquisition of the (Continued on page 15)
1. Find bugs.
In cooperation with:
Page 18
configuration is shown in Figure 1 (note that the calling of the
Writing Filters... redirectors for the name resolution step is not depicted).
Have a great product design, but looking for extra security to validate internal operations before bringing it to
your board of directors? Or perhaps you’re in the late stages of development of your driver and are looking to
have an expert pour over the code to ensure stability and robustness before release to your client base.
A small investment in time and money in either service can ―save face‖ in front of those who will be contributing
to your bottom line. OSR has worked with both startups and multi-national behemoths. Consider what a team of
internals, device driver and file system experts can do for you. Contact OSR Sales — [email protected].
Page 21
Not All Stories Have a Happy Ending
Writing Filters... Unfortunately, we continue fighting with DFS to this day and
have open bugs for configurations that do not work and, quite
possibly, might never be made to work. Complicating things
(Continued from page 20)
for us is that MUP and DFS have been redesigned on
Believe it or not, what MUP actually does is dig into the Windows Vista and later such that filters are no longer sit in
device object to which that the create IRP was sent, finds the between MUP and the redirector. Because the design has
driver object, and captures the driver object name. This driver moved forward, we have no chance of getting any changes
object name is then compared to the hardcoded value, made to legacy platforms. Until every last one of our clients
―\Filesystem\Mup‖. If the names match, MUP knows that he has upgraded to Vista and later, we‘re going to be stuck trying
just reparsed the create back to himself and can handle it
to hammer filter drivers into an architecture that clearly wasn‘t
internally. However, if the names do not match then MUP
allows the reparse to travel back to the I/O Manager. designed to interact with other components in the system.
If you have to debug and analyze such problems all the time, there are fundamentals and techniques you can
learn with training and experience. This is exactly what we offer as part of our 5-day Kernel Debugging & Crash
Analysis seminar. But, if you aren’t charged with regularly root-causing such problems and/or don’t have the
luxury of time to build up knowledge and a repertoire of techniques, having an expert set of eyes do the work for
you, and in a timely fashion, could be a big win. Think about that hang that has been plaguing your development
staff for weeks. Or that customer-reported BSOD that has them ready to walk out the door. Think it’s time to
escalate?
This is what OSR offers as part of its Problem Analysis Service. For a $2,499 fixed-price, submit your issue and
receive initial triage within 2 business days and a report within a week!
More information on this service, including a sample Problem Analysis Report is available at
www.osr.com/analyze.html or by contacting OSR’s Sales Department at [email protected].
Page 22
an SRB SenseInfoBuffer has been specified, the driver
Getting Better... should be filling it correctly.
Crash
Alas, during his testing Mike managed
to get the driver to crash. This
problem has been fixed in the new
code drop. It turns out that the code
in CreateConnection that added a
new connection to the list of current
connections did not synchronize
access to the ConnectionList. Thus
if you added and removed connections
Summary
In our article series Writing a Virtual Storport Miniport Driver
we described key aspects of the architecture, design, and OSR would like to thank Mike Berhan of busTRACE Technologies
implementation of this type of driver. We've built on that for taking the time to test and analyze the OSR Virtual Storport
series, and made the driver more compliant, with the tweaks Miniport Driver and for providing us with a copy of busTRACE so
described in this article. And while these tweaks might not be that we could find and fix the issues discussed in this article.
absolutely critical, they do make the driver more compliant Without the help of Mike and his fine tool, we wouldn't have
known about the driver's failure to conform to the specifications.
NTSTATUS ProcessScsiCommandError(PSCSI_REQUEST_BLOCK PSrb)
{
NTSTATUS status = STATUS_SUCCESS;
if(PSrb->SenseInfoBuffer && PSrb->SenseInfoBufferLength) {
PSENSE_DATA pSense = (PSENSE_DATA) PSrb->SenseInfoBuffer;
RtlZeroMemory(pSense,PSrb->SenseInfoBufferLength);
pSense->ErrorCode = 0x70;
pSense->Valid = 0;
pSense->SenseKey = SCSI_SENSE_ILLEGAL_REQUEST;
pSense->AdditionalSenseLength = 0x15;
pSense->AdditionalSenseCode = SCSI_ADSENSE_ILLEGAL_COMMAND;
pSense->AdditionalSenseCodeQualifier = 0;
PSrb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
PSrb->SrbStatus = SRB_STATUS_AUTOSENSE_VALID | SRB_STATUS_ERROR;
} else {
status = STATUS_UNSUCCESSFUL;
PSrb->SrbStatus = SRB_STATUS_ERROR;
}
return status;
}
Figure 5—ProcessScsiCommandError
Ask us to perform any of the above activities for your company, and you will be pleased with the definitive answer
or result we provide. Ask us almost anything about user-mode development, Linux or where the world economy
will be in five years, and what you will get is an opinion, laughter or both.
So, the only question WE have is, ―How can we help you?‖ Contact: [email protected]
Page 24
//
Debugger Extensions... hr = debugSymbols->GetFieldOffset
(module,
typeId,
"CurrentThread",
&fieldOffset);
(Continued from page 9)
if (hr != S_OK) {
// debugSymbols->Release();
hr = debugControl->Evaluate("2 + 2", return hr;
DEBUG_VALUE_INT32, }
&result,
NULL); debugControl->Output
(DEBUG_OUTCTL_ALL_CLIENTS,
if (hr != S_OK) { "Offset of CurrentThread is %d\n",
debugControl->Release(); fieldOffset);
return hr;
} debugSymbols->Release();
return S_OK;
debugControl->Output(DEBUG_OUTCTL_ALL_CLIENTS, }
"Result is %d\n",
result.I32);
Dealing with 32-bit vs. 64-bit
// Note that when you‘re writing a debugger extension
// Done with this. command, your code is always running on the host machine.
//
debugControl->Release(); Also note that the pointer size of the host machine does not
necessarily match the pointer size of the target machine, due
return S_OK;
} to the fact that 32-bit hosts can debug 64-bit targets and vice
versa. In order to deal with this, debugger extensions treat all
Hopefully the steps followed are fairly straightforward at this addresses from the target as 64-bit values. Thus you‘ll note
point. We‘ve taken the passed-in client object, created an that the DbgEng APIs express pointer addresses as ULONG64
instance of IDebugControl, and then executed some methods values. Any 32-bit value used in your extension command
on it to perform actions. must be sign extended out to a full 64-bit value.
To further drive home the pattern, we can see how we‘d get Alternative Debugger Extension Interfaces
the offset of a field of a data structure. For that, we need an To add to the confusion when it comes to writing a debugger
instance of IDebugSymbols: extension, there are two alternative interfaces that you can use
to write your debugger extensions. The first is the WdbgExts
HRESULT CALLBACK interface, which is the legacy debugger extension interface
myothercommand(PDEBUG_CLIENT4 Client, PCSTR args)
{ that existed before DbgEng came around. This interface is still
PDEBUG_SYMBOLS debugSymbols; available in the newest versions of the debugger, however it
HRESULT hr; has two drawbacks. First, it is not the forward moving API
ULONG fieldOffset;
ULONG typeId; thus it is frozen in time and will provide no new features.
ULONG64 module; Second, this interface does not have any support for writing
UNREFERENCED_PARAMETER(args); standalone applications, thus it won‘t port to any kind of
automated analysis tool that you might write. If you‘re
// interested in learning more about the legacy extension model,
// Let's find the offset of the CurrentThread see this article on OSR Online.
// field of the PRCB
//
hr = Client->QueryInterface The other interface available to you is the EngExtCpp
(__uuidof(IDebugSymbols), interface. This is actually just a C++ wrapper library around
(void **)&debugSymbols);
DbgEng to simplify common tasks such as manipulating typed
if (hr != S_OK) { data. Unlike WdbgExts, this is a fully supported interface and
return hr;
} can be used alongside the direct DbgEng calls that we‘ve
discussed in this article.
//
// We need the "type identifier" and module
// containing the symbol that we're interested Go forth!
// in.
// Hopefully we‘ve been able to clear up the cloud of mystery
hr = debugSymbols->GetSymbolTypeId("nt!_KPRCB", that hangs over writing debugger extensions and have set you
&typeId, on a journey of creating your own.
&module);
if (hr != S_OK) { Source to a debug extension (!uniqstack) can be downloaded
debugSymbols->Release();
return hr; from: http://www.osronline.com/OsrDown.cfm/apexts.zip?
}
name=apexts.zip&id=559
//
// Now we can get the offset.
Page 25
access directory (e.g., ―C:\Windows‖) the performance of the
Getting Away... system can become so poor that your driver will be deemed
―unusable‖ as a result. In such situations we‘ve been forced to
add caching to the size correction process. Even that will not
(Continued from page 11) make the first enumeration of a directory ―fast‖ however.
Directory size correction is an area we will leave for a future
programs. In Figure 2 we show what appears to be a pair of document and discussion, because it is itself surprisingly
files – one of these is 16 bytes long and the other is 54 bytes complicated.
long. In fact, there really is only one file as we have created a
hard link. If we access the file via the hard link, we can see Summary
the size change (in Figure 3). In future installments as we build our sample, we will focus
on the mechanisms necessary to manage our shadow file
This is because NTFS actually stores the file sizes within the objects, contexts and section object pointer structures.
directory entry – but when a file contains hard links, only the Stay tuned!
link that is actually used is updated. Despite this, applications
rely upon correct size information and in some cases they use
it for validation.
http://www.osronline.com/custom.cfm?name=login_j
oinok.cfm
Figure 3
Page 26
m_SharedWaitersCount – number of threads
EResources... waiting to acquire the resource in shared mode.
m_ExclusiveWaitersCount – number of threads
waiting to acquire the resource in exclusive mode.
(Continued from page 15) m_Initialized – Boolean indicating whether or not
the resource was initialized by a call to
resource. Since threads can hold the resource OSRInitializeResource.
recursively this does not necessarily reflect the
number of threads holding the resource The code which implements this class uses the
simultaneously. m_CriticalSection to synchronize access to the rest of the
m_StarveExclusiveCount – number of threads OSREResource data, while the m_LockStateMap is used to
waiting to acquire the resource in shared mode, but keep track of each holder of the resource, the mode of
want to starve exclusive waiters. acquisition (State), and the number of times that the
m_ExclusiveCount – number of exclusive holders of acquisition was made since a holder could acquire the
the resource, which is usually one, but since a thread resource recursively (AcquireCount). The data members‘
can acquire resource lock recursively, this count m_SharedCount and m_ExclusiveCount are also used to
would reflect that. keep track of the acquisition counts.
// This structure keeps track of the state of the resource. We need For any thread which tries to
// to know how the resource was acquired and how many times it was
// recursively acquired. acquire the OSREResource and
// elects to wait for the resource,
typedef struct _OSRERESOURCE_STATE
{ the m_WaitEventHandle is the
union { handle to the event that the
struct { thread will wait on. This event
ULONG State; // Acquisition State
ULONG AcquireCount; // Count of times acquired is called a ―NotificationEvent‖.
}; What this means is that unlike a
ULONGLONG Reserved;
}; ―SynchronizationEvent‖ where
} OSRERESOURCE_STATE, *POSRERESOURCE_STATE; only the first waiting thread is
woken up, when the event is set,
class OSREResource : public CObject all waiting threads are woken up.
{
public: This means that each awakened
OSREResource(); thread has the possibility of
virtual ~OSREResource(); acquiring the resource. Consider
enum { the scenario where a thread holds
ACQUIRED_NONE, the resource exclusively and 10
ACQUIRED_SHARED,
ACQUIRED_EXCLUSIVE threads are waiting to acquire the
} ACQUIRED_STATE; resource shared. In our
BOOLEAN AcquireResourceExclusive(BOOLEAN Wait); implementation all the shared
BOOLEAN AcquireResourceShared(BOOLEAN Wait); waiters will be awakened when
BOOLEAN AcquireSharedStarveExclusive(BOOLEAN Wait); the exclusive holder releases the
BOOLEAN IsResourceAcquiredExclusive();
BOOLEAN IsResourceAcquiredShared(); resources and will be able to
VOID ReleaseResource(); acquire the resource.
VOID ConvertExclusiveToShared();
VOID ReleaseResourceForThread(ERESOURCE_THREAD ResourceThreadId);
ULONG GetSharedWaiterCount(); Since the source code is readily
ULONG GetExclusiveWaiterCount();
NTSTATUS InitializeResource(); available for download, we don‘t
NTSTATUS DeleteResource(); want to spend a lot of time going
private: through it. What we do want to
mutable CRITICAL_SECTION m_CriticalSection; do however is go over
typedef std::map<DWORD,OSRERESOURCE_STATE> CMapThreadToLockState;
AcquireResourceShared and
CMapThreadToLockState m_LockStateMap; ReleaseResource to at least give
you a feel for how the code
volatile DWORD m_SharedCount;
volatile DWORD m_StarveExclusiveCount; works.
volatile DWORD m_ExclusiveCount;
volatile DWORD m_SharedWaitersCount;
volatile DWORD m_ExclusiveWaitersCount; AcquireResourceShared
HANDLE m_WaitEventHandle; Figure 3 (page 27) contains the
BOOLEAN m_Initialized;
}; AcquireResourceShared
function. As the name implies,
Figure 2—OSREResource Class and OSRERESOURCE_STATE Structure this function attempts to acquire
(Continued on page 27)
Page 27
EResources... This routine first looks to see if the calling thread already
holds the resource by looking for the threads‘ Id in the
m_LockStateMap. If the thread is found, then the code
(Continued from page 26) updates the ownership counts and returns TRUE to the caller
indicating that the resource has been acquired.
the OSREResource shared and if it cannot, waits if the user
indicated wait for the resource to become available. If the thread Id was not found in the m_LockStateMap, the
Remember that our code acquires m_CriticalSection to code then checks to see if the resource is held exclusively or if
ensure proper synchronization when access the class data there are threads waiting for the resource exclusively. If there
structures. (Continued on page 28)
m_SharedWaitersCount++;
while(TRUE) {
// See if we already own the lock.
//
CMapThreadToLockState::iterator ite = m_LockStateMap.find(cThread);
if(ite != m_LockStateMap.end()) {
//
// We already own it, let's see what access we already have.
//
ite->second.AcquireCount++;
if(ite->second.State & ACQUIRED_SHARED) {
// We already have it shared, so just bump the count.
//
m_SharedCount++;
} else if(ite->second.State & ACQUIRED_EXCLUSIVE) {
// We have it exclusive, so just give it to the caller exclusive
//
m_ExclusiveCount++;
}
m_SharedWaitersCount--;
LeaveCriticalSection(&m_CriticalSection);
return TRUE;
} else if(m_ExclusiveCount || m_ExclusiveWaitersCount) {
// Someone else owns it exclusive or there are exclusive waiters, so we have to wait.
//
if(!Wait) {
m_SharedWaitersCount--;
LeaveCriticalSection(&m_CriticalSection);
break;
}
LeaveCriticalSection(&m_CriticalSection);
WaitForSingleObject(m_WaitEventHandle,INFINITE);
// Add a wait loop to prevent this thread from hogging the CPU. The event gets set
// when the holder releases the lock and we have to ensure that everyone gets a chance
// to run before continuing.
//
loopCount++;
if(loopCount == 5) {
Sleep(200);
loopCount = 0;
}
EnterCriticalSection(&m_CriticalSection);
continue;
} else {
// Nobody has it or wants it, so we will take it. We reset the event, so that it is no
// longer signaled. Anyone who was waiting for the event should already be woken up.
//
OSRERESOURCE_STATE state;
state.State = ACQUIRED_SHARED;
state.AcquireCount = 1;
m_SharedCount++;
m_SharedWaitersCount--;
m_LockStateMap.insert(std::make_pair(cThread,state));
ResetEvent(m_WaitEventHandle);
LeaveCriticalSection(&m_CriticalSection);
return TRUE;
}
}
return FALSE;
} Figure 3—AcquireResourceShared
Page 28
EResources... There is one thing that we must highlight here and it is that the
loop code follows the WaitForSingleObject. Keep in mind
that there could be many threads, with various scheduling
(Continued from page 27) priorities, wanting to acquire the OSREResource in different
ways, all waiting for it to become free. If we automatically
are, then this indicates that the caller cannot acquire the gave acquisition of the OSREResource to the first thread
resource. What is done next depends on the input Wait through the code, we could potentially starve other waiters,
parameter. If this parameter is TRUE, then the caller will since we know that highest priority will almost always get the
wait on the m_WaitEventHandle. If this parameter is CPU first. We didn‘t want to keep track of the waiters in a
FALSE, the code will return to the caller indicating that the list and heuristically pick the next owner, so we decided to
resource was not acquired. make it somewhat random (BTW, if you don‘t like it, change
it and let us know what your solution is and why it is better!).
Finally, if the resource is not held exclusively, or if there are Therefore we have all threads coming out of the
no threads waiting for the resource exclusively, then the code WaitForSingleObject sleeping for about 200 milliseconds, to
inserts an entry into the m_LockStateMap. This entry let all threads that were blocked have time to run through the
indicates how the resource was acquired, updates the acquire code again and reassess their access to the lock.
appropriate counts in the class, and returns to the caller
indicating that the resource was acquired. That‘s all there is to acquiring the OSREResource shared, so
let‘s see how you release it.
ReleaseResource
VOID OSREResource::ReleaseResource() Figure 4 contains the code for
{
DWORD cThread = GetCurrentThreadId(); ReleaseResource. As the name
implies, this code releases an owned
EnterCriticalSection(&m_CriticalSection);
OSREResource. Remember that
// our code acquires m_Critical
// See if we own the lock. We'd better.....
// Section to ensure proper
CMapThreadToLockState::iterator ite = m_LockStateMap.find(cThread); synchronization when accessing the
if(ite != m_LockStateMap.end()) {
class data structures.
//
// We own it. Decrement the ownership count based upon The first step for the code is to
// our access.
// attempt to find the calling thread in
if(ite->second.State & ACQUIRED_SHARED) { the m_LockStateMap, which keeps
m_SharedCount--;
} else { track of the threads that are currently
m_ExclusiveCount--; holding the OSREResource. If the
} calling thread is not found in the
// m_LockStateMap structure, the
// Look at our ownership count in the lowpart of the code will raise a 0xC0000002
// large integer. If it is 1, then this is our
// last reference to the lock, so we will delete exception to indicate that the
// ourselves from the ownership map. OSREResource was not held by the
//
if(ite->second.AcquireCount == 1) { caller. If the caller was found in the
// m_LockStateMap, the code will
// erase us, we no longer own the lock.
// decrement either the m_Shared
m_LockStateMap.erase(ite); Count or m_ExclusiveCount field,
//
depending upon how the caller had
// Wake up anyone waiting on access to the lock. acquired the OSREResource.
//
SetEvent(m_WaitEventHandle);
} else { The code then looks at whether or
// not this is the calling thread‘s last
// We still have outstanding references....
// reference to the OSREResource,
ite->second.AcquireCount--; which would indicate that the
} OSREResource is now free to be
LeaveCriticalSection(&m_CriticalSection);
} else { acquired by other threads. It checks
RaiseException(0xC0000002,EXCEPTION_NONCONTINUABLE,0,NULL); for the last reference by looking at
}
} the AcquiredCount field of the
returned OSRERESOURCE
Figure 4—ReleaseResource _STATE structure. If the
(Continued on page 29)
Page 29
These were added to aid in debugging problems that you may
EResources... run into and are shown in Figure 5. As for deadlocks, the
only way to debug those is to examine each thread in your
application and see what it is waiting for in hopes that you can
(Continued from page 28) determine the deadlock.
Problems
As we mentioned earlier, the code will raise different
exceptions if it determines that something bad is going on.
To take advantage of our expertise in Windows internals, and in instructional design, contact an OSR seminar
consultant at +1.603.595.6500 or by email at [email protected].
Page 30
Analyst’s Perspective
Debug Smarter
O K, OK, I‘ll admit it…Debugging crash dumps can get
tedious fast. In a recent system hang that I analyzed, there
were 1,200 threads in the system. Given no details on what
So, I think it‘s time for a regime change. Let‘s promote
debugging to first class citizenship and spend the time
necessary to make our lives easier. Let‘s declare August 2010
was going on at the time of the hang, my eyes would probably the Month of Debugging Smarter and start rethinking how we
start bleeding before I found the threads that were interesting. approach solving our debugging problems. See P 8 of this
And that wasn‘t even a big terminal server machine with lots issue for an article on the basics of writing a debugger
of sessions running, so it was fairly tame by modern extension and, to get your creative juices flowing, there‘s even
standards. the source of a kernel mode implementation of !uniqstack for
you to play with. This command scans all of the threads in the
This is of course why we rely on our automated tools to do the system and, when finished, provides the !thread output of the
heavy lifting for us. When the system crashes, we don‘t go threads in the system with unique call stack sequences. As an
look up the bugcheck code and start trying to decode trap example of how many threads this eliminates, my 1,200
frames or context records on the stack, we instead rely on ! thread system ended up only having 105 unique call chain
analyze –v to do this work for us. In my 1,200 thread case, I sequences, which is certainly much more manageable.
didn‘t bother taking a detailed look at every thread in the
system but instead relied on !stacks 2 to give me a summary So no more excuses about not knowing how to write your own
of threads so that I could quickly scan for something that extension! And, if you do write your own extension, email me
looked ―interesting.‖ at [email protected] to let me know about it. Hopefully I can gather
submissions from The NT Insider readership and put together
What I think we often lose sight of though is that these some interesting tidbits for our next issue.
commands aren‘t magic. At some point in time, someone
thought that their job would be made easier if there was a
command that would quickly provide summary analysis
information at the time of a crash. Someone else thought
sifting through the output of !process 0 7 was also far too Analyst’s Perspective is a column by OSR consulting
painful and came up with a command to provide a summary associate, Scott Noone. When he’s not root-causing
view. complex kernel issues he’s leading the development and
instruction of OSR’s Kernel Debugging & Crash Analysis
Why is it that we don‘t all think this way? Instead, we tend to seminar. Comments on this article, or suggestions for a
rely on the existing commands and then complain that they future submission can be addressed to [email protected].
don‘t work the way we‘d like. Between WinDBG‘s scripting
capabilities and its debugger extension support it‘s hard to say
that there is anything it can‘t do. The common responses to
that though are usually, ―the scripting language is too cryptic‖ Kernel Debugging & Crash Analysis
or, ―I don‘t know how to write an extension.‖ But, in reality,
any language is cryptic until you take the time to learn it and You’ve seen our articles where we delve into analyses
most of us didn‘t know how to put pants on at some point in of various crash dumps or system hangs to determine
our lives. However, one day we decided that it was an
root cause. Want to learn the tools and techniques
important skill and decided to look at some examples and
practice (if you still don‘t know how to put pants on, I yourself? Consider attendance at OSR’s Kernel
apologize and you are deemed exempt from the remainder of Debugging & Crash Analysis seminar. This hands-on
this perspective). training provides fundamentals as well as valued
techniques that are used and espoused internally at
The other trap that we all fall into is that we just don‘t allow OSR.
ourselves the time to write a script or extension that will
potentially save us hours down the road. When debugging a
difficult problem, we tend to get tunnel vision and refuse to The next offering of this seminar is to be held in:
tear ourselves away from the problem to do something so
frivolous. I definitely get dragged down into this one, and by Portland, OR 18-22 October
the time I‘ve figured the problem out the extension idea leaves
my mind. Until I get the next dump of course, at which point I For more information, visit www.osr.com/debug.html
really wish I had written that extension…
Page 31
in Bolivia and needs a safe place to store his secret
Peter Pontificates... communications.‖ You code to these particular stories. No,
you don‘t get a chance to think through the overall experience
for any user. This is Agile software development. You don‘t
(Continued from page 5) get to think. You‘re not allowed to design. You‘re allowed to
―get some code working‖ so you can try things out. And that
User stories are the software equivalent of task based code just needs to meet the user stories, and pass the tests that
documentation – another stoopid fad that I truly despise. But, were so lovingly crafted and stored with those stories.
you know, manager-troids love task-based documentation for Anything else? Well, that‘s for next sprint.
the same reasons they love user stories for software
development: It‘s easy to understand, it‘s easy to demonstrate So, in Agile what you get are ridiculously incomplete
that it‘s correct (just follow the steps and see if they work), requirements, driving a development process that emphasizes
and you can get an idiot to do it. The problem? It doesn‘t sloppy implementation over design, that‘s tracked using a
consider anything that‘s not specifically required for you to long list of bogus and irrelevant milestones. The only thing
achieve the task. For example, you can write a document that‘s left to wonder about is why everybody doesn‘t think
chapter entitled ―How to Peel an Onion‖ that describes the Agile development sucks ass.
process in detail. It will cover all the steps. But, on finishing
the chapter, the reader hasn‘t learned anything about onions,
peeling, or anything else. They‘ve just learned how to peel an
onion, and even then only under the conditions that the
chapter contemplates, using the steps the chapter prescribes. Peter Pontificates is a regular opinion column by OSR
consulting partner, Peter Viscarola. Peter doesn’t care if you
And so it is for user stories. Let‘s say you get a couple of user agree or disagree, but you do have the opportunity to
stories like ―Bob is a church-going motorcycle rider that needs respond and perhaps even see your comments or a rebuttal
to store digital copies of his ‗special‘ magazines in encrypted in a future issue. Send your own comments, rants or
form on his flash drive‖ and ―Hector served with Che Guevara distortions of fact to: [email protected].
Consulting Toolkits
In consultative engagements, OSR works with OSR software development toolkits provide
clients to determine needs and provide options to solutions that package stable, time-testing
proceed with OSR, or suggest alternative technology, with support from an engineering staff
solutions external to OSR. “Consulting" assistance that has helped dozens of customers deliver
from OSR can be had in many forms, but no successful solutions to market.
matter how it is acquired, you can be assured that
we'll be bringing our definitive expertise, industry
experience, and solid reputation to bear on our
engagement with you.
More information on OSR products and services can be found at the www.osr.com.
Page 32
OSR OPEN SYSTEMS RESOURCES, INC. PRESORTED
105 State Route 101A, Suite 19 FIRST CLASS MAIL
Amherst, New Hampshire 03031 USA U.S. POSTAGE
PAID
(603)595-6500 ♦ Fax (603)595-6503 PERMIT # 456
MANCHESTER, NH