1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
|
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);
|