One of the more useful features of kdb is its ability to print the
arguments to functions, unlike the kernel which only prints an
approximation of the function backtrace.  The current kdb backtrace
does not cope with i386 parameters in registers (i386 built with
-mregparm=3), nor does it cope with x86_64 where the ABI mandates that
the first 6 parameters are passed in registers.  I will be rewriting
the backtrace code for i386/x86_64 to cope with parameters in
registers.

kdb cannot rely on unwind data in the .eh_frame section.  i386 has only
just added that data (2.6.17-rc1), not all kernels are built with
CONFIG_UNWIND_INFO but worst of all, CONFIG_UNWIND_INFO=y does not
actually track where parameters are stored.

gcc can copy arguments between registers, it can store arguments in
local stack space and it can restore them to other registers later.
gcc can even reuse argument registers when the argument is no longer
required, printing such registers as arguments can lead to some very
ambiguous backtraces and great user confusion.

To track the parameters, you have to compile the entire kernel with -g,
and load that data for kdb to use.  Compiling with -g increases the
kernel size by a factor of 5.  Without -g, a reasonably small i386
vmlinux is 6760001, with -g it is 33209257, too much wasted space for
my liking.  Not to mention that there is no useful debugging data for
assembler routines such as interrupt handlers, nor for the out of line
spinlock contention paths.

The current kdb backtrace routines already do partial code analysis at
run time to work out stack sizes, to validate the guesses at where
the return address is stored on stack and to handle all the special
case hand crafted assembler code that we have in the kernel.  The
rewrite will go one stage further and do full code analysis, including
tracking parameters as gcc moves them around.

For each function in the backtrace, kdb will break it down into basic
blocks and calculate the state on entry to and exit from each basic
block.  kdb will then use that state to locate the arguments, or to
report that they have been overwritten.  The state is also used to
track the size of the stack, as a nice side effect this guarantees that
we find the correct return address on stack and do not get fooled by
stale kernel pointers on stack.

A basic block (BB) starts at the beginning of the function or at any
instruction that is the target of a branch instruction within the
current function.  A call to another function does not affect the
current BB, except to invalidate some registers.  A branch to an
instruction outside the current function is ignored, these occur for
tail recursion and for the out of line spinlock contention paths,
neither of which affect the state of the current BB.

A BB ends with an unconditional branch, a RET, LEAVE or UD2
instruction, or by dropping through to the next BB (the next line is
the target of a branch instruction).  A BB can also exit sideways via a
conditional branch.

A state record contains data such as :-

  The offset of the current stack pointer from the original value on
  entry.

  Where the original stack pointer was saved.  This is preferred to the
  adjusted stack pointer, but only if the kernel is compiled with frame
  pointers and we have actually executed the instruction that saves the
  original stack pointer.

  For each argument, where it is currently held or a note that it has
  been overwritten.  An argument can exist in multiple locations at the
  same time, the status of all of these locations must be tracked
  because kdb does not know which will be used later and which will be
  overwritten.

Phase 1 runs the current function to identify the basic blocks and the
branch instructions that link them together.  This forms a directed
graph, which may or may not have cycles.  For each BB, count the number
of ways that it can be entered and reserve enough space to point to the
state records for each of those entries, plus one more.

Phase 2 runs the directed graph, doing backtracking as cycles are
detected.  kdb starts off by assuming that i386 routines are compiled
with -mregparm=3, and that x86_64 routines follow the ABI and pass 6
parameters in registers.  Each BB is disassembled and the effect of
each instruction is reflected in the state record, but only if the
instruction changes any of the state that kdb cares about.  At each
exit point from the BB, the current state record is saved to form the
input to the next BB in the graph.

Phase 3 reconciles the input state when a BB has more than one entry
point.  One path to a BB may overwrite an argument, another path to the
same BB may leave the argument alone.  Reconciliation takes the lowest
state for that argument and says that the argument is not valid on
entry to the BB.  Compiler theory 101, if two paths to a BB have an
argument in an inconsistent state then the BB cannot be using the
argument.  It should be possible to fold the state reconciliation into
the phase 2 processing, but treat it as a separate phase for clarity.

Phase 4 calculates the number of arguments and works out how they are
passed to this function.  kdb does not know how many parameters are
passed, it does not even know whether arguments to this function are in
registers or on stack, some functions always get parameters on stack
even when the kernel is compiled with -mregparm=3.  If any BB reads
from an argument register without first writing to that register then
it must contain an argument, and all preceding argument registers are
also in use.  If a BB reads from the stack above the location
containing the return address then it is reading an argument that was
passed on stack, all locations between that address and the return
address also contain arguments.  This algorithm is not perfect,
function(a, b, c, d) compiled with -mregparm=3 and where only d is
used, will appear to have one argument that was passed on stack.
Fortunately we do not write code that bad.

Finally identify the starting state of the BB containing the current
instruction pointer and calculate the argument state at the instruction
pointer.  Print the function and all arguments that kdb is confident
about.

Extract the return pointer from the stack.  With full code analysis kdb
knows exactly where this is stored, which removes any ambiguity caused
by stale kernel pointers that are still on stack.  This return address
becomes the starting point for decoding the next function up the call
tree, back to phase 1.

Even with this algorithm, kdb will still need special case code for
some of the assembler routines and for out of line code.  No big deal,
it is a Kernel DeBugger after all :).

---------------------------
Use http://oss.sgi.com/ecartis to modify your settings or to unsubscribe.

Reply via email to