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