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 all 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:
// 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.
r $t0=0— this sets$t0to zero@$t0 < 0n19— this stops once$t0reaches 19.r $t0=@$t0+1— this increments$t0.
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— this command dumps a singly-linked list.poi (fffff8a0`06b69930+@$t0*8)— this is the head of the linked list. In this example,0xfffff8a0`06b69930is the address of thebucketsarray, so we add the loop counter times the size of a pointer (8, in this case) to get the address of the$t0'th entry, and then dereference it (poi) to get the address of the head of the linked list.99— This is the maximum length of the linked list. I picked an arbitrary large-enough number. I like using 9's because it carries the most value per keypress. Other people like to use nice round numbers like1000, but999saves you a whole keypress and is just one less. (On the other hand, I don't usefffbecause that runs the risk of being misinterpreted as a symbol.)2— This is the number of pointer-sized objects to dump at the start of each node. For 32-bit code, I use 4, whereas for 64-bit code, I use 2. Why those values? Because those are the maximum number of pointer-sized objects that the debugger will print on one line.
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.
fffff8a0`037a4700 fffff8a0`01f96fb0 00000000`0000003f
Now I have my ENTRY, and I can dump it to see what
the corresponding value is.
0: kd> dt contoso!ENTRY fffff8a0`037a4700
+0x000 next: 0xfffff8a0`01f96fb0
+0x008 atom: 0x0000003f
+0x010 value: 0xffff8a0`01f97e20 -> "foo"