193 lines
7.3 KiB
Plaintext
193 lines
7.3 KiB
Plaintext
|
|
Xserver Debugging
|
|
=================
|
|
|
|
This file is intended to collect helpful hints on Xserver debugging.
|
|
I merely outline my experiences here. Somebody else might have better
|
|
methods on doing it. This person is therefore invited to share this
|
|
experience with the rest of the world by adding it here.
|
|
|
|
Paul Flinders has made some patches to gdb to add support for loadable
|
|
modules. This version of gdb is currently available as binary for
|
|
Linux/x86 on Paul's web site:
|
|
|
|
www.dawa.demon.co.uk/xfree-gdb
|
|
|
|
This web-site also contains the patches to gdb 4.18 so you may port it
|
|
to other platforms.
|
|
|
|
It loads the module symbols and supports all gdb features like
|
|
breakpointing, disassembling and single stepping. It also shows the
|
|
exact location of a signal 11. Paul has fixed the code so that all of
|
|
this is working even if using modules compiled without -g. You can
|
|
find his latest version on his web site.
|
|
|
|
If no module aware gdb is available the following hints might help:
|
|
|
|
1. Use remote login. This can be done thru a network connection or
|
|
simply by connecting a serial console. This enables you to watch
|
|
the Xservers output while running set breakpoints with gdb etc.
|
|
Don't even try to run the Xserver from a system console. Whenever
|
|
something happens gdb waits for input. However the Xserver has
|
|
locked the system console including the keyboard, therefore you'll
|
|
never be able to send any input to gdb. Even if your process
|
|
doesn't crash or you haven't set any breakpoints a vt switch can be
|
|
hazardous: When doing vt switching a signal is sent; unless you did
|
|
|
|
gdb> handle SIGUSR1 nostop
|
|
|
|
gdb waits for you to continue the program which cannot happen as
|
|
you don't have access to gdb's console.
|
|
|
|
2. You can compile any source file with debugging symbols to obtain
|
|
more information about where an error occurred. Simply go to the
|
|
directory which holds the corresponding object file and do:
|
|
|
|
# rm <file>.o
|
|
# xc/config/util/makeg.sh <file>.o
|
|
|
|
After relinking the server or module gdb is able to obtain the
|
|
necessary debugging information and will show the exact line in the
|
|
source where the error ccurred. See also:
|
|
xc/config/util/makeg.man.
|
|
|
|
3. In some cases it might be useful to have the assembler output of a
|
|
compiled source file. This can be obtained by doing:
|
|
|
|
# make <file>.s
|
|
|
|
or
|
|
|
|
# xc/config/util/makeg.sh <file>.s
|
|
|
|
Make will use exactly the same rules it uses for building *.o files.
|
|
|
|
4. In some cases it might be useful to set breakpoints in modules. If
|
|
no module aware gdb is available you should add a call to one of
|
|
the three dummy breakpoint functions
|
|
|
|
xf86Break1(), xf86Break2() and xf86Break3()
|
|
|
|
to the source file and recompile the module. You now just have to
|
|
set a breakpoint onto the appropriate dummy functions. These
|
|
functions are located in the core part of the server and therefore
|
|
will be available any time.
|
|
|
|
5. Without module support gdb is not able to print the function where
|
|
an error occurred in a module.
|
|
|
|
If you get a line like:
|
|
|
|
(gdb) bt
|
|
#0 0x823b4f5 in ?? ()
|
|
....
|
|
|
|
You may obtain the function the address belongs to by calling
|
|
LoaderPrintSymbol():
|
|
|
|
(gdb) call LoaderPrintSymbol(0x823b4f5)
|
|
|
|
The symbol returned might not always be the name of the function
|
|
which contains the address. In case of static functions the symbol
|
|
is not known to the loader. However LoaderPrintSymbol() will print
|
|
the nearest known function and the offset from its start. You may
|
|
easily find the exact location of the address if you do:
|
|
|
|
# objdump --disassemble <file>.o
|
|
|
|
<file>.o is the name of the object file containing the symbol printed.
|
|
|
|
6. Locating static symbols in modules is simpler if the module is a
|
|
single object file instead of a library. Such a object file can
|
|
easily be build from a library: # mkdir tmp # cd tmp; ar x
|
|
module-path/<libname>.a # ld -r *.o -o module-path/<name>.o
|
|
|
|
When calling LoaderPrintSymbol() the closes public symbol will be
|
|
printed together with the offset from the symbol's address. If a
|
|
static symbol comes before the first public symbol in a module The
|
|
following trick may help:
|
|
|
|
create a file 1-<name>.c in tmp/
|
|
containing:
|
|
void Dummy-<name>() {}
|
|
|
|
Compile it:
|
|
|
|
# gcc -c 1-<name>.c
|
|
|
|
and do the link step above.
|
|
|
|
This way Dummy-<name>() will be the first public function in the
|
|
module. All addresses in static function can now be printed
|
|
relatively to this address if no other public function comes before
|
|
this static one.
|
|
|
|
7. In some situations it is quite helpful to add debugging symbols to
|
|
the binary. This can be done per object file. Simply remove the
|
|
object file and do
|
|
|
|
# makeg
|
|
|
|
When looking for a bug in a module these debugging infos can be
|
|
very helpful: Calling LoaderPrintSymbol() as described above will
|
|
return a function and an offset giving the exact location of the
|
|
address with respect to this function entry point. When
|
|
disassembling an object file with debugging symbols: # objdump -d
|
|
-l <file>.o one will receive a disassembled output containing line
|
|
number information. Thus one can locate the exact line of code
|
|
where the error occurred.
|
|
|
|
8. To quickly trace the value of a variable declared in a module three
|
|
dummy variables have been added to the core part:
|
|
|
|
CARD32 xf86DummyVar1;
|
|
CARD32 xf86DummyVar2;
|
|
CARD32 xf86DummyVar3;
|
|
|
|
The variable can be assigned to one of them. One can then use gdb
|
|
to return the value of this variable:
|
|
|
|
gdb> p /x xf86DummyVar1
|
|
|
|
9. Sometimes it might be useful to check how the preprocessor replaced
|
|
symbols. One can obtain a preprocessed version of the source file
|
|
by doing:
|
|
|
|
make <filename>.i
|
|
|
|
This will generate a preprocessed source in <filename>.i.
|
|
|
|
10. xfree() can catch if one tries to free a memory range twice. You
|
|
will get the message:
|
|
|
|
Xalloc error: range already freed in Xrealloc() :-(
|
|
|
|
To find the location from which xfree() was called one can
|
|
breakpoint on XfreeTrap(). The backtrace should show the origin of the
|
|
call this call.
|
|
|
|
11. To access mapped physical memory the following functions might be
|
|
useful.
|
|
|
|
These may be used to access physical memory that was mapped using
|
|
the flags VIDMEM_FRAMEBUFFER or VIDMEM_MMIO32:
|
|
|
|
CARD8 xf86PeekFb8(CARD8 *p);
|
|
CARD16 xf86PeekFb16(CARD16 *p);
|
|
CARD32 xf86PeekFb32(CARD32 *p);
|
|
void xf86PokeFb8(CARD8 *p, CARD8 v);
|
|
void xf86PokeFb16(CARD16 *p, CARD16 v);
|
|
void xf86PokeFb32(CARD16 *p, CARD32 v);
|
|
|
|
Physical memory which was mapped by setting VIDMEM_MMIO should be
|
|
accessed using the following. Here the base address to which the
|
|
memory is mapped and the offset are required separately.
|
|
|
|
CARD8 xf86PeekMmio8(pointer Base, unsigned long Offset);
|
|
CARD16 xf86PeekMmio16(pointer Base, unsigned long Offset);
|
|
CARD32 xf86PeekMmio32(pointer Base, unsigned long Offset);
|
|
void xf86PokeMmio8(pointer Base, unsigned long Offset, CARD8 v);
|
|
void xf86PokeMmio16(pointer Base, unsigned long Offset, CARD16 v);
|
|
void xf86PokeMmio32(pointer Base, unsigned long Offset, CARD32 v);
|
|
|