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