DebuggingHints   [plain text]



                        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);