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