1 |
689 |
jeremybenn |
/* Infrastructure to test the quality of debug information.
|
2 |
|
|
Copyright (C) 2008, 2009, 2010 Free Software Foundation, Inc.
|
3 |
|
|
Contributed by Alexandre Oliva <aoliva@redhat.com>.
|
4 |
|
|
|
5 |
|
|
This file is part of GCC.
|
6 |
|
|
|
7 |
|
|
GCC is free software; you can redistribute it and/or modify
|
8 |
|
|
it under the terms of the GNU General Public License as published by
|
9 |
|
|
the Free Software Foundation; either version 3, or (at your option)
|
10 |
|
|
any later version.
|
11 |
|
|
|
12 |
|
|
GCC is distributed in the hope that it will be useful,
|
13 |
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14 |
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15 |
|
|
GNU General Public License for more details.
|
16 |
|
|
|
17 |
|
|
You should have received a copy of the GNU General Public License
|
18 |
|
|
along with GCC; see the file COPYING3. If not see
|
19 |
|
|
<http://www.gnu.org/licenses/>. */
|
20 |
|
|
|
21 |
|
|
#include <stdio.h>
|
22 |
|
|
#include <stdlib.h>
|
23 |
|
|
#include <string.h>
|
24 |
|
|
#include <stdint.h>
|
25 |
|
|
#include <unistd.h>
|
26 |
|
|
|
27 |
|
|
/* This is a first cut at checking that debug information matches
|
28 |
|
|
run-time. The idea is to annotate programs with GUALCHK* macros
|
29 |
|
|
that guide the tests.
|
30 |
|
|
|
31 |
|
|
In the current implementation, all of the macros expand to function
|
32 |
|
|
calls. On the one hand, this interferes with optimizations; on the
|
33 |
|
|
other hand, it establishes an optimization barrier and a clear
|
34 |
|
|
inspection point, where previous operations (as in the abstract
|
35 |
|
|
machine) should have been completed and have their effects visible,
|
36 |
|
|
and future operations shouldn't have started yet.
|
37 |
|
|
|
38 |
|
|
In the current implementation of guality_check(), we fork a child
|
39 |
|
|
process that runs gdb, attaches to the parent process (the one that
|
40 |
|
|
called guality_check), moves up one stack frame (to the caller of
|
41 |
|
|
guality_check) and then examines the given expression.
|
42 |
|
|
|
43 |
|
|
If it matches the expected value, we have a PASS. If it differs,
|
44 |
|
|
we have a FAILure. If it is missing, we'll have a FAIL or an
|
45 |
|
|
UNRESOLVED depending on whether the variable or expression might be
|
46 |
|
|
unavailable at that point, as indicated by the third argument.
|
47 |
|
|
|
48 |
|
|
We envision a future alternate implementation with two compilation
|
49 |
|
|
and execution cycles, say one that runs the program and uses the
|
50 |
|
|
macros to log expressions and expected values, another in which the
|
51 |
|
|
macros expand to nothing and the logs are used to guide a debug
|
52 |
|
|
session that tests the values. How to identify the inspection
|
53 |
|
|
points in the second case is yet to be determined. It is
|
54 |
|
|
recommended that GUALCHK* macros be by themselves in source lines,
|
55 |
|
|
so that __FILE__ and __LINE__ will be usable to identify them.
|
56 |
|
|
*/
|
57 |
|
|
|
58 |
|
|
/* This is the type we use to pass values to guality_check. */
|
59 |
|
|
|
60 |
|
|
typedef intmax_t gualchk_t;
|
61 |
|
|
|
62 |
|
|
/* Convert a pointer or integral type to the widest integral type,
|
63 |
|
|
as expected by guality_check. */
|
64 |
|
|
|
65 |
|
|
#ifndef __cplusplus
|
66 |
|
|
#define GUALCVT(val) \
|
67 |
|
|
((gualchk_t)__builtin_choose_expr \
|
68 |
|
|
(__builtin_types_compatible_p (__typeof (val), gualchk_t), \
|
69 |
|
|
(val), \
|
70 |
|
|
__builtin_choose_expr \
|
71 |
|
|
(__builtin_classify_type (val) \
|
72 |
|
|
== __builtin_classify_type (&guality_skip), \
|
73 |
|
|
(uintptr_t)(val),(intptr_t)(val))))
|
74 |
|
|
#else
|
75 |
|
|
template <typename T>
|
76 |
|
|
inline __attribute__((always_inline)) gualchk_t
|
77 |
|
|
gualcvt (T *val)
|
78 |
|
|
{
|
79 |
|
|
return (uintptr_t) val;
|
80 |
|
|
}
|
81 |
|
|
|
82 |
|
|
template <typename T>
|
83 |
|
|
inline __attribute__((always_inline)) gualchk_t
|
84 |
|
|
gualcvt (T val)
|
85 |
|
|
{
|
86 |
|
|
return (intptr_t) val;
|
87 |
|
|
}
|
88 |
|
|
|
89 |
|
|
template <>
|
90 |
|
|
inline __attribute__((always_inline)) gualchk_t
|
91 |
|
|
gualcvt<gualchk_t> (gualchk_t val)
|
92 |
|
|
{
|
93 |
|
|
return val;
|
94 |
|
|
}
|
95 |
|
|
|
96 |
|
|
#define GUALCVT(val) gualcvt (val)
|
97 |
|
|
#endif
|
98 |
|
|
|
99 |
|
|
/* Attach a debugger to the current process and verify that the string
|
100 |
|
|
EXPR, evaluated by the debugger, yields the gualchk_t number VAL.
|
101 |
|
|
If the debugger cannot compute the expression, say because the
|
102 |
|
|
variable is unavailable, this will count as an error, unless unkok
|
103 |
|
|
is nonzero. */
|
104 |
|
|
|
105 |
|
|
#define GUALCHKXPRVAL(expr, val, unkok) \
|
106 |
|
|
guality_check ((expr), (val), (unkok))
|
107 |
|
|
|
108 |
|
|
/* Check that a debugger knows that EXPR evaluates to the run-time
|
109 |
|
|
value of EXPR. Unknown values are marked as acceptable,
|
110 |
|
|
considering that EXPR may die right after this call. This will
|
111 |
|
|
affect the generated code in that EXPR will be evaluated and forced
|
112 |
|
|
to remain live at least until right before the call to
|
113 |
|
|
guality_check, although not necessarily after the call. */
|
114 |
|
|
|
115 |
|
|
#define GUALCHKXPR(expr) \
|
116 |
|
|
GUALCHKXPRVAL (#expr, GUALCVT (expr), 1)
|
117 |
|
|
|
118 |
|
|
/* Same as GUALCHKXPR, but issue an error if the variable is optimized
|
119 |
|
|
away. */
|
120 |
|
|
|
121 |
|
|
#define GUALCHKVAL(expr) \
|
122 |
|
|
GUALCHKXPRVAL (#expr, GUALCVT (expr), 0)
|
123 |
|
|
|
124 |
|
|
/* Check that a debugger knows that EXPR evaluates to the run-time
|
125 |
|
|
value of EXPR. Unknown values are marked as errors, because the
|
126 |
|
|
value of EXPR is forced to be available right after the call, for a
|
127 |
|
|
range of at least one instruction. This will affect the generated
|
128 |
|
|
code, in that EXPR *will* be evaluated before and preserved until
|
129 |
|
|
after the call to guality_check. */
|
130 |
|
|
|
131 |
|
|
#define GUALCHKFLA(expr) do { \
|
132 |
|
|
__typeof(expr) volatile __preserve_after; \
|
133 |
|
|
__typeof(expr) __preserve_before = (expr); \
|
134 |
|
|
GUALCHKXPRVAL (#expr, GUALCVT (__preserve_before), 0); \
|
135 |
|
|
__preserve_after = __preserve_before; \
|
136 |
|
|
asm ("" : : "m" (__preserve_after)); \
|
137 |
|
|
} while (0)
|
138 |
|
|
|
139 |
|
|
/* GUALCHK is the simplest way to assert that debug information for an
|
140 |
|
|
expression matches its run-time value. Whether to force the
|
141 |
|
|
expression live after the call, so as to flag incompleteness
|
142 |
|
|
errors, can be disabled by defining GUALITY_DONT_FORCE_LIVE_AFTER.
|
143 |
|
|
Setting it to -1, an error is issued for optimized out variables,
|
144 |
|
|
even though they are not forced live. */
|
145 |
|
|
|
146 |
|
|
#if ! GUALITY_DONT_FORCE_LIVE_AFTER
|
147 |
|
|
#define GUALCHK(var) GUALCHKFLA(var)
|
148 |
|
|
#elif GUALITY_DONT_FORCE_LIVE_AFTER < 0
|
149 |
|
|
#define GUALCHK(var) GUALCHKVAL(var)
|
150 |
|
|
#else
|
151 |
|
|
#define GUALCHK(var) GUALCHKXPR(var)
|
152 |
|
|
#endif
|
153 |
|
|
|
154 |
|
|
/* The name of the GDB program, with arguments to make it quiet. This
|
155 |
|
|
is GUALITY_GDB_DEFAULT GUALITY_GDB_ARGS by default, but it can be
|
156 |
|
|
overridden by setting the GUALITY_GDB environment variable, whereas
|
157 |
|
|
GUALITY_GDB_DEFAULT can be overridden by setting the
|
158 |
|
|
GUALITY_GDB_NAME environment variable. */
|
159 |
|
|
|
160 |
|
|
static const char *guality_gdb_command;
|
161 |
|
|
#define GUALITY_GDB_DEFAULT "gdb"
|
162 |
|
|
#if defined(__unix)
|
163 |
|
|
# define GUALITY_GDB_REDIRECT " > /dev/null 2>&1"
|
164 |
|
|
#elif defined (_WIN32) || defined (MSDOS)
|
165 |
|
|
# define GUALITY_GDB_REDIRECT " > nul"
|
166 |
|
|
#else
|
167 |
|
|
# define GUALITY_GDB_REDIRECT ""
|
168 |
|
|
#endif
|
169 |
|
|
#define GUALITY_GDB_ARGS " -nx -nw --quiet" GUALITY_GDB_REDIRECT
|
170 |
|
|
|
171 |
|
|
/* Kinds of results communicated as exit status from child process
|
172 |
|
|
that runs gdb to the parent process that's being monitored. */
|
173 |
|
|
|
174 |
|
|
enum guality_counter { PASS, INCORRECT, INCOMPLETE };
|
175 |
|
|
|
176 |
|
|
/* Count of passes and errors. */
|
177 |
|
|
|
178 |
|
|
static int guality_count[INCOMPLETE+1];
|
179 |
|
|
|
180 |
|
|
/* If --guality-skip is given in the command line, all the monitoring,
|
181 |
|
|
forking and debugger-attaching action will be disabled. This is
|
182 |
|
|
useful to run the monitor program within a debugger. */
|
183 |
|
|
|
184 |
|
|
static int guality_skip;
|
185 |
|
|
|
186 |
|
|
/* This is a file descriptor to which we'll issue gdb commands to
|
187 |
|
|
probe and test. */
|
188 |
|
|
FILE *guality_gdb_input;
|
189 |
|
|
|
190 |
|
|
/* This holds the line number where we're supposed to set a
|
191 |
|
|
breakpoint. */
|
192 |
|
|
int guality_breakpoint_line;
|
193 |
|
|
|
194 |
|
|
/* GDB should set this to true once it's connected. */
|
195 |
|
|
int volatile guality_attached;
|
196 |
|
|
|
197 |
|
|
/* This function is the main guality program. It may actually be
|
198 |
|
|
defined as main, because we #define main to it afterwards. Because
|
199 |
|
|
of this wrapping, guality_main may not have an empty argument
|
200 |
|
|
list. */
|
201 |
|
|
|
202 |
|
|
extern int guality_main (int argc, char *argv[]);
|
203 |
|
|
|
204 |
|
|
static void __attribute__((noinline))
|
205 |
|
|
guality_check (const char *name, gualchk_t value, int unknown_ok);
|
206 |
|
|
|
207 |
|
|
/* Set things up, run guality_main, then print a summary and quit. */
|
208 |
|
|
|
209 |
|
|
int
|
210 |
|
|
main (int argc, char *argv[])
|
211 |
|
|
{
|
212 |
|
|
int i;
|
213 |
|
|
char *argv0 = argv[0];
|
214 |
|
|
|
215 |
|
|
guality_gdb_command = getenv ("GUALITY_GDB");
|
216 |
|
|
if (!guality_gdb_command)
|
217 |
|
|
{
|
218 |
|
|
guality_gdb_command = getenv ("GUALITY_GDB_NAME");
|
219 |
|
|
if (!guality_gdb_command)
|
220 |
|
|
guality_gdb_command = GUALITY_GDB_DEFAULT GUALITY_GDB_ARGS;
|
221 |
|
|
else
|
222 |
|
|
{
|
223 |
|
|
int len = strlen (guality_gdb_command) + sizeof (GUALITY_GDB_ARGS);
|
224 |
|
|
char *buf = (char *) __builtin_alloca (len);
|
225 |
|
|
strcpy (buf, guality_gdb_command);
|
226 |
|
|
strcat (buf, GUALITY_GDB_ARGS);
|
227 |
|
|
guality_gdb_command = buf;
|
228 |
|
|
}
|
229 |
|
|
}
|
230 |
|
|
|
231 |
|
|
for (i = 1; i < argc; i++)
|
232 |
|
|
if (strcmp (argv[i], "--guality-skip") == 0)
|
233 |
|
|
guality_skip = 1;
|
234 |
|
|
else
|
235 |
|
|
break;
|
236 |
|
|
|
237 |
|
|
if (!guality_skip)
|
238 |
|
|
{
|
239 |
|
|
guality_gdb_input = popen (guality_gdb_command, "w");
|
240 |
|
|
/* This call sets guality_breakpoint_line. */
|
241 |
|
|
guality_check (NULL, 0, 0);
|
242 |
|
|
if (!guality_gdb_input
|
243 |
|
|
|| fprintf (guality_gdb_input, "\
|
244 |
|
|
set height 0\n\
|
245 |
|
|
attach %i\n\
|
246 |
|
|
set guality_attached = 1\n\
|
247 |
|
|
b %i\n\
|
248 |
|
|
continue\n\
|
249 |
|
|
", (int)getpid (), guality_breakpoint_line) <= 0
|
250 |
|
|
|| fflush (guality_gdb_input))
|
251 |
|
|
{
|
252 |
|
|
perror ("gdb");
|
253 |
|
|
abort ();
|
254 |
|
|
}
|
255 |
|
|
}
|
256 |
|
|
|
257 |
|
|
argv[--i] = argv0;
|
258 |
|
|
|
259 |
|
|
guality_main (argc - i, argv + i);
|
260 |
|
|
|
261 |
|
|
i = guality_count[INCORRECT];
|
262 |
|
|
|
263 |
|
|
fprintf (stderr, "%s: %i PASS, %i FAIL, %i UNRESOLVED\n",
|
264 |
|
|
i ? "FAIL" : "PASS",
|
265 |
|
|
guality_count[PASS], guality_count[INCORRECT],
|
266 |
|
|
guality_count[INCOMPLETE]);
|
267 |
|
|
|
268 |
|
|
return i;
|
269 |
|
|
}
|
270 |
|
|
|
271 |
|
|
#define main guality_main
|
272 |
|
|
|
273 |
|
|
/* Tell the GDB child process to evaluate NAME in the caller. If it
|
274 |
|
|
matches VALUE, we have a PASS; if it's unknown and UNKNOWN_OK, we
|
275 |
|
|
have an UNRESOLVED. Otherwise, it's a FAIL. */
|
276 |
|
|
|
277 |
|
|
static void __attribute__((noinline))
|
278 |
|
|
guality_check (const char *name, gualchk_t value, int unknown_ok)
|
279 |
|
|
{
|
280 |
|
|
int result;
|
281 |
|
|
|
282 |
|
|
if (guality_skip)
|
283 |
|
|
return;
|
284 |
|
|
|
285 |
|
|
{
|
286 |
|
|
volatile gualchk_t xvalue = -1;
|
287 |
|
|
volatile int unavailable = 0;
|
288 |
|
|
if (name)
|
289 |
|
|
{
|
290 |
|
|
/* The sequence below cannot distinguish an optimized away
|
291 |
|
|
variable from one mapped to a non-lvalue zero. */
|
292 |
|
|
if (fprintf (guality_gdb_input, "\
|
293 |
|
|
up\n\
|
294 |
|
|
set $value1 = 0\n\
|
295 |
|
|
set $value1 = (%s)\n\
|
296 |
|
|
set $value2 = -1\n\
|
297 |
|
|
set $value2 = (%s)\n\
|
298 |
|
|
set $value3 = $value1 - 1\n\
|
299 |
|
|
set $value4 = $value1 + 1\n\
|
300 |
|
|
set $value3 = (%s)++\n\
|
301 |
|
|
set $value4 = --(%s)\n\
|
302 |
|
|
down\n\
|
303 |
|
|
set xvalue = $value1\n\
|
304 |
|
|
set unavailable = $value1 != $value2 ? -1 : $value3 != $value4 ? 1 : 0\n\
|
305 |
|
|
continue\n\
|
306 |
|
|
", name, name, name, name) <= 0
|
307 |
|
|
|| fflush (guality_gdb_input))
|
308 |
|
|
{
|
309 |
|
|
perror ("gdb");
|
310 |
|
|
abort ();
|
311 |
|
|
}
|
312 |
|
|
else if (!guality_attached)
|
313 |
|
|
{
|
314 |
|
|
unsigned int timeout = 0;
|
315 |
|
|
|
316 |
|
|
/* Give GDB some more time to attach. Wrapping around a
|
317 |
|
|
32-bit counter takes some seconds, it should be plenty
|
318 |
|
|
of time for GDB to get a chance to start up and attach,
|
319 |
|
|
but not long enough that, if GDB is unavailable or
|
320 |
|
|
broken, we'll take far too long to give up. */
|
321 |
|
|
while (--timeout && !guality_attached)
|
322 |
|
|
;
|
323 |
|
|
if (!timeout && !guality_attached)
|
324 |
|
|
{
|
325 |
|
|
fprintf (stderr, "gdb: took too long to attach\n");
|
326 |
|
|
abort ();
|
327 |
|
|
}
|
328 |
|
|
}
|
329 |
|
|
}
|
330 |
|
|
else
|
331 |
|
|
{
|
332 |
|
|
guality_breakpoint_line = __LINE__ + 5;
|
333 |
|
|
return;
|
334 |
|
|
}
|
335 |
|
|
/* Do NOT add lines between the __LINE__ above and the line below,
|
336 |
|
|
without also adjusting the added constant to match. */
|
337 |
|
|
if (!unavailable || (unavailable > 0 && xvalue))
|
338 |
|
|
{
|
339 |
|
|
if (xvalue == value)
|
340 |
|
|
result = PASS;
|
341 |
|
|
else
|
342 |
|
|
result = INCORRECT;
|
343 |
|
|
}
|
344 |
|
|
else
|
345 |
|
|
result = INCOMPLETE;
|
346 |
|
|
asm ("" : : "X" (name), "X" (value), "X" (unknown_ok), "m" (xvalue));
|
347 |
|
|
switch (result)
|
348 |
|
|
{
|
349 |
|
|
case PASS:
|
350 |
|
|
fprintf (stderr, "PASS: %s is %lli\n", name, value);
|
351 |
|
|
break;
|
352 |
|
|
case INCORRECT:
|
353 |
|
|
fprintf (stderr, "FAIL: %s is %lli, not %lli\n", name, xvalue, value);
|
354 |
|
|
break;
|
355 |
|
|
case INCOMPLETE:
|
356 |
|
|
fprintf (stderr, "%s: %s is %s, expected %lli\n",
|
357 |
|
|
unknown_ok ? "UNRESOLVED" : "FAIL", name,
|
358 |
|
|
unavailable < 0 ? "not computable" : "optimized away", value);
|
359 |
|
|
result = unknown_ok ? INCOMPLETE : INCORRECT;
|
360 |
|
|
break;
|
361 |
|
|
default:
|
362 |
|
|
abort ();
|
363 |
|
|
}
|
364 |
|
|
}
|
365 |
|
|
|
366 |
|
|
switch (result)
|
367 |
|
|
{
|
368 |
|
|
case PASS:
|
369 |
|
|
case INCORRECT:
|
370 |
|
|
case INCOMPLETE:
|
371 |
|
|
++guality_count[result];
|
372 |
|
|
break;
|
373 |
|
|
|
374 |
|
|
default:
|
375 |
|
|
abort ();
|
376 |
|
|
}
|
377 |
|
|
}
|