Windbg Tip: KN, .Frame , DV, and DT – It’s so easy

Written by Jeff Dailey.

Hello NTDebuggers, many of us take for granted some of the simple commands in the debugger that make life easy. I was thinking of several in particular that go great together.  The first command would be kn.  Kn will show the current call stack and includes the stack frame number to the far left of the individual call.  

I loaded up a copy of BadWindow.EXE in Windbg, set my thread to thread Zero via ~0s.  Then I simply type kn.  This dumps out the call stack, and as you can see, to the far left each call there is the stack frame number.

Using stack frame 2 from the above stack let’s look at the locals it uses.  First we must get in context using .frame and the frame number.

Now to dump out the locals we can just use the dv (Display Local Variables) command.  (Note you need private symbols for the binary you are debugging for this to work).

As you can see, we have several locals here.  Now, because I have private symbols for this binary, the debugger already understands the structures and data types for each variable.

Furthermore because I’m already in the stack frames context I can simply dt (display type) with the name of the variable and the debugger figures out the address.  Let’s dt msg and see what data it contains.

As you can see  have a couple of structures that are part of this structure.    Look at the right in the above output.  You will notice a data type name of HWND_ and tagPOINT to the right.  These are structures.  The good news is that dt will work recursively and will dump out structures under your current structure.   All you need to do is issue a DT –r.  You can specify the depth by appending a number to –r.

For more information about the dt command and others mentioned in this blog make sure you take a look at the debugger.chm file that comes with the Windows Debugging Tools.

Good luck and happy debugging.

Comments (3)
  1. Skywing says:

    I’m very surprised that you recommend this given how extremely unreliable local variable display is.

    In fact, my recommendation is to never at all use “dv”, because depending on where you are in the function when you execute that command, and whether the compiler was feeling tricky with optimizing things in a sufficiently clever way, it may either fail to work completely, or worse, return invalid information.

    I’ve ran into persons getting misread by this (bogus data returned by local variables) time after time here – to the point where more often than not, it wastes far more hours than it saves in work in manually figuring out where variables are with a quick glance at the disassembly.

    Please, stay away, far away, from local variable support.  The only time when you have a reasonable shot of it working is if you working with a non-optimized debug build (not a typical for debugging a real world problem at a customer site).  Furthermore, even with debug builds, if you are at the wrong place in the function prolog, “dv”, will *still* return bogus data.

    [ Actually, you’re right. In some cases dv may report an incorrect value. However that is generally in x64/ia64 builds. This is because most 64 bit processors make heavy use of registers for locals and for parameter passing. Now, if you build a checked build on these platforms you will most likely see vast improvements in dv’s ability to report local data for your stack frames.

    Another factor is symbols. If you’re using the Microsoft public symbol servers they do not include parameter or local information. Applications that you build yourself would obviously contain all the debug data needed to decode the locals with dv.

    Overall we typically see dv report very accurate locals on x86 platforms, and while looking at the assembler will give you definitive info about your locals, it is more time consuming. The dv offset information is almost always correct. As you stated, dv’s accuracy is typically a factor of the state of execution relative to your stack frame.

    Almost everyone that debugs here in GES uses dv. If we see a value that does not look right (i.e. you dv it and the values look bogus), then you know you have a local that is not valid. In such cases we do just as you stated: look at the assembler. Most of us look at dumps or do live debugs all day long, so dv is a big time saver and allows you to quickly inspect the various stack frames. That said, be sure to eyeball the values, and if they don’t look right, dig in.

    On a side note, I want a quick view of a thread’s overall state I’ll do “!for_each_frame dv”  Thanks for your feedback and Happy debugging. ]

  2. Skywing says:

    I actually run into this problem quite a lot with x86 programs that build with WPO/LTCG style optimizations (which we do by default), and with which most of the work is not done by exported functions.

    I suppose your milage may very, but cl likes to optimize away parameters to be register only frequently and/or cache locals in registers only, at least with full optimizations turned on.  Some of this is of course slightly reduced in scope for exported functions that must maintain the specified calling convention due to unknown external references.

    Personally, however, I’ve ran into bogus dv data, and seen others burn quite a lot of time due to assuming that dv data is infalliable, at least with a lot of the debugging we’ve done here.

    It’s certainly a handy feature for debug builds if you are at a source line sequence point, but even on x86, the compiler typically performs optimizations beyond the ability of the .pdb to describe correctly to the debugger.

    Custom calling conventions frequently make even ‘kv’ unreliable for parameter data as cl will very often place one or two parameters in one of the volatile registers.  Of course, if you are mostly looking at exported APIs that are well and truly bound to be the calling convention that you specify at compile time, and not internal non-exported routines, this becomes less of an issue.

    To give an idea, however, I just pulled up one of our real world dumps here at work that I looked at a few days ago, and for a callstack for a thread that was 6 frames deep into non-exported code (with the last frame being BaseThreadStart, discounting that as I don’t have private symbols for it), there were 33 locals to be shown with "!for_each_frame dv".

    Of these locals shown, after I spent some time manually verifying each value shown by the debugger, there were at least 13 that were bogus, some of which were not obviously so without doing a bit of investigative work to show that given the call frame, such a value as the debugger claimed would not have been possible given the dump file.

    That’s a >1/3 chance of someone looking at that dump to get bad data out of dv, and some of those were again not "immediately obvious" that they were invalid without doing more than a glance at the variable values.  Other dump files I have looked at have had nontrivial amounts of the thinks displayed in dv to be bogus as well.  If you like I can go get some statistics on some more dump files, although it’s a bit of a time consuming process to manually go and validate every local to see if the debug info was sufficient to show the right values.

    This was a program built with full optimizations on VS2005SP1, x86 build.  It has still been my experience that most dumps we get are of this nature in terms of reliability of dv (or lack thereof).  The compiler is simply very aggressive at optimizations that are beyond the ability for the debug info format and the debugger to cope with, even on x86 – again, in my personal experience.

    dv can certainly have it’s uses, but I would really stop short of calling it "very accurate" on x86 if you’re dealing with an optimized build.

  3. Dan says:

    In your screenshots, the prompt and input are white, but the output is green–how did you do that?

Comments are closed.

Skip to main content