Debugger commands (stack frame navigation) that makes my life easier

One thing that I have always found clunky is stack frame navigation in windbg/kd.
Previously, I thought you had only a couple of options.

The first option, if you are using WinDBG, is that
you can bring up the call stack window. I have found that this is not a great
thing to do b/c WinDBG will try to update the call stack window while the target
is running and not just when you are broken in (perhaps it does this only for
module loads, I don't know). Of course, since this is WinDBG dependent, this does
not work in kd. As the final straw, this requires moving my hands off the keyboard,
losing valuable time navigating with the mouse ;).

The second option is to use the kn
command. This will dump the stack with frame numbers, the output looks like this:

1: kd> kn
# ChildEBP RetAddr
00 81e33c6c 81898d7c nt!RtlpBreakWithStatusInstruction
01 81e33c74 81898d2e nt!KdCheckForDebugBreak+0x22
02 81e33d20 8183ddd5 nt!KeUpdateRunTime+0x270
03 81e33d50 8187dba2 nt!PopIdleDefaultHandler+0x239
04 81e33d54 00000000 nt!KiIdleLoop+0xa

And then you can issue the .frame N command,
where N is the frame number to navigate to that frame and start debugging

1: kd> .frame 3
03 81e33d50 8187dba2 nt!PopIdleDefaultHandler+0x239
1: kd> dv

My problem is that I didn't know how to do frame relative jumps, so every time
I wanted to move to a new frame I ran kn again
(since I forget the frame numbers), found the new frame and ran
.frame N with the new frame number. Well today I
saw 2 new ways how to navigate the frames.

The first way will work with the previous debugger releases. You use the pseudo register
$frame. Pretty nice trick IMO.

1: kd> .frame @$frame-1
02 81e33d20 8183ddd5 nt!KeUpdateRunTime+0x270
1: kd> .frame @$frame+1
03 81e33d50 8187dba2 nt!PopIdleDefaultHandler+0x239

The second way to navigate frames requires the debugger package that was just
released and is a little less cumbersome to use. There are 2 new debugger commands,
.f+ and .f-.
These are definitely easier to use in my book because I don't have to remember
the register deref syntax.

1: kd> .f-
02 81e33d20 8183ddd5 nt!KeUpdateRunTime+0x270
1: kd> .f+
03 81e33d50 8187dba2 nt!PopIdleDefaultHandler+0x239

With these new commands, I think I get now get by without having to lift my
hands off the keyboard ;).

Comments (9)

  1. swamyv says:

    It is nice to read this blog topic.

    When I go up and down the stack using ".f-" or ".f+" the register values should

    change from a frame to frame right. It does not change. It always shows

    the top stack frame register values. Is this a windbg bug or is there a different command to get the local frame register values.

    Where are the register information saved?

    Is it saved in the stacka and if so at what location it is saved and how do I get the register values.



  2. registers are not saved per call (unless there is a trap, in which case the ‘kv’ & ‘.trap’ commands can restore the context).  the calling standard dictates which registers are volatile and which are restored during a function call.  walking the stack and finding the individual files that were pushed onto the stack is an upcoming topic (a little too complicated for a comment).  


  3. swamyv says:

    Thanks for your quick reply.

    Good thing here is at least we can get the context of trap frame.  But I tried the

    the .trap <address> and it throws syntax error (on windows amd64). I do not understand why. The address I used was the SP of trap frame.

    On unix the we can navigate the frames using "up" or  "down" commands

    and get the local frame registers. I thought it is possible here also.


  4. where did you get the address for .trap?  from the ‘kv’ command?

    are the registers that important b/c you want to see where your locals/parameters are stored?  if so, the ‘dv’ does a pretty job of displaying those values.  On an optimized build you might have a harder time finding the locals/parameters, but it is not too hard to find them with a little disassembly


  5. swamyv says:

    I used the first field which is Child SP and also tried the second one which is

    return address. May be something wrong. It is not taking any address value.


    .trap 05234fc0

    Yes the register values are important because to see all the local variables and also you can see from the reg values you can see what code has executed.

    It is hard but it helps to diagnose a problem.

    I tried dv and it just dumped one symbol and not very useful in the crash I am

    looking at.

    Anyway thanks for your help.

  6. you can’t take any random value and use it for .trap.  you need to get the trap frame pointer from the ‘kv’ command (it is listed all the way on the right, after the function name).  a trap frame is not generated on every call, they are generated when something "exceptional" happens (like a context switch or a potentially invalid memory access in the kernel).

    if you are only seeing one local from ‘dv’, then you are using an optimized build or your symbols have this information stripped.

  7. swamyv says:

    Yes I know the frame where trap occured and I see that frame

    below KiUserExceptionDispatcher and also the TrapFrame pointer

    next to it. I tried that address too. I also tried different

    possible formats and it just reports a SyntaxError. This is on

    windbg on amd64.  I have not tried it on other platform.  This

    might work on other there. Instead of just printing an SyntaxError

    it should print more meaning full error would help.

    Yes, I am using optimized build.  

  8. treynash says:

    With regards to register contents at each stack frame…..

    When you are debugging x64 code, you can use the ".frame /r" variant to show what the non-volatile register contents were at that stack frame.  This is essential for figuring out values of locals in optimized code since x64 has so many registers and uses them liberally.

    Additionally, there is the ".frame /c" variant which is similar to ".frame /r" except that it changes the register context similar to the .cxr command.  To me, ".frame /c" this is of limited use.

    More details at the following link:


Skip to main content