Holy cow, I wrote a book!
I was doing some crash dump debugging, as I am often called upon to do, and one of the data structure I had to grovel through was something that operated basically like an atom table, so that's what I'll call it. Like an atom table, it manages a collection of strings. You can add a string to the table (getting a unique value back, which we will call an atom), and later you can hand it the atom and it will give you the string back. It looked something like this:
struct ENTRY { ENTRY *next; UINT atom; PCWSTR value; }; #define ATOMTABLESIZE 19 struct ATOMTABLE { ENTRY *buckets[ATOMTABLESIZE]; };
(It didn't actually look like this; I've reduced it to the smallest example that still illustrates my point.)
As part of my debugging, I had an atom and needed to look it up in the table. A program would do this by simply calling the "here is an atom, please give me the string" function, but since this was a crash dump, there's nothing around to execute anything. (Not that having a live machine would've made things much easier, because this was a kernel-mode crash, so you don't get any of this edit-and-continue froofy stuff. This is real debugging™.)
But even though the crashed system can't execute anything, the debugger can execute stuff, and the debugger engine used by kd comes with its own mini-programming language. Here's how I dumped the atom table:
kd
// note: this was entered all on one line // broken into two lines for readability 0: kd> .for (r $t0=0; @$t0 < 0n19; r $t0=@$t0+1) { dl poi (fffff8a0`06b69930+@$t0*8) 99 2 } fffff8a0`06ad3b90 fffff8a0`037a3fc0 fffff8a0`0000000c \ fffff8a0`037a3fc0 fffff8a0`037a4ae0 00000000`00000025 | $t0=0 fffff8a0`037a4ae0 fffff8a0`02257580 00000000`00000036 | fffff8a0`02257580 00000000`00000000 00000000`00000056 / fffff8a0`06ad3b30 fffff8a0`06ad3ad0 a9e8a9d8`0000000d \ fffff8a0`06ad3ad0 fffff8a0`037a4700 000007fc`0000000e | fffff8a0`037a4700 fffff8a0`01f96fb0 00000000`0000003f | $t0=1 fffff8a0`01f96fb0 fffff8a0`06cfa5d0 fffff8a0`00000044 | fffff8a0`06cfa5d0 00000000`00000000 00181000`00000060 / fffff8a0`033e7a70 fffff8a0`037a4770 00000020`00000023 \ fffff8a0`037a4770 fffff8a0`023b52f0 00000000`0000003e | $t0=2 fffff8a0`023b52f0 fffff8a0`03b6e020 006f0063`00000059 | fffff8a0`03b6e020 00000000`00000000 00000000`00000075 / fffff8a0`037a0670 fffff8a0`02b08870 fffff8a0`00000026 \ $t0=3 fffff8a0`03b9e390 00000000`00000000 00240000`00000071 / ...
Let's take that weirdo command apart one piece at a time.
The overall command is
.for (a; b; c) { d }
This operates the same as the C programming language. (Sorry, Delphi programmers, for yet another C-centric example.) In our case, we use the $t0 pseudo-register as our loop control.
$t0
r $t0=0
@$t0 < 0n19
r $t0=@$t0+1
Note that here as well as in the loop body, I prefix the register name with the @ character when I want to obtain its value, in order to force it to be interpreted as a register. (Otherwise, the debugger will look for a symbol called $t0.)
@
The command being executed at each iteration is { dl poi (fffff8a0`06b69930+@$t0*8) 99 2 }. Let's break this down, too:
{ dl poi (fffff8a0`06b69930+@$t0*8) 99 2 }
dl
poi (fffff8a0`06b69930+@$t0*8)
0xfffff8a0`06b69930
buckets
poi
99
1000
999
fff
2
Okay, so now I have that linked list dump. The value I'm looking for is the atom whose value is 0x3F, so I skim down the last column looking for 0000003f, and hey there it is.
atom
0x3F
0000003f
fffff8a0`037a4700 fffff8a0`01f96fb0 00000000`0000003f
Now I have my ENTRY, and I can dump it to see what the corresponding value is.
ENTRY
0: kd> dt contoso!ENTRY fffff8a0`037a4700 +0x000 next: 0xfffff8a0`01f96fb0 +0x008 atom: 0x0000003f +0x010 value: 0xffff8a0`01f97e20 -> "foo"