btest.c revision 1.1
1/* btest.c -- Test for libbacktrace library
2   Copyright (C) 2012-2013 Free Software Foundation, Inc.
3   Written by Ian Lance Taylor, Google.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are
7met:
8
9    (1) Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11
12    (2) Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in
14    the documentation and/or other materials provided with the
15    distribution.
16
17    (3) The name of the author may not be used to
18    endorse or promote products derived from this software without
19    specific prior written permission.
20
21THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31POSSIBILITY OF SUCH DAMAGE.  */
32
33/* This program tests the externally visible interfaces of the
34   libbacktrace library.  */
35
36#include <assert.h>
37#include <stdio.h>
38#include <stdlib.h>
39#include <string.h>
40
41#include "filenames.h"
42
43#include "backtrace.h"
44#include "backtrace-supported.h"
45
46/* Portable attribute syntax.  Actually some of these tests probably
47   won't work if the attributes are not recognized.  */
48
49#ifndef GCC_VERSION
50# define GCC_VERSION (__GNUC__ * 1000 + __GNUC_MINOR__)
51#endif
52
53#if (GCC_VERSION < 2007)
54# define __attribute__(x)
55#endif
56
57#ifndef ATTRIBUTE_UNUSED
58# define ATTRIBUTE_UNUSED __attribute__ ((__unused__))
59#endif
60
61/* Used to collect backtrace info.  */
62
63struct info
64{
65  char *filename;
66  int lineno;
67  char *function;
68};
69
70/* Passed to backtrace callback function.  */
71
72struct bdata
73{
74  struct info *all;
75  size_t index;
76  size_t max;
77  int failed;
78};
79
80/* Passed to backtrace_simple callback function.  */
81
82struct sdata
83{
84  uintptr_t *addrs;
85  size_t index;
86  size_t max;
87  int failed;
88};
89
90/* Passed to backtrace_syminfo callback function.  */
91
92struct symdata
93{
94  const char *name;
95  uintptr_t val;
96  int failed;
97};
98
99/* The backtrace state.  */
100
101static void *state;
102
103/* The number of failures.  */
104
105static int failures;
106
107/* Return the base name in a path.  */
108
109static const char *
110base (const char *p)
111{
112  const char *last;
113  const char *s;
114
115  last = NULL;
116  for (s = p; *s != '\0'; ++s)
117    {
118      if (IS_DIR_SEPARATOR (*s))
119	last = s + 1;
120    }
121  return last != NULL ? last : p;
122}
123
124/* Check an entry in a struct info array.  */
125
126static void
127check (const char *name, int index, const struct info *all, int want_lineno,
128       const char *want_function, int *failed)
129{
130  if (*failed)
131    return;
132  if (strcmp (base (all[index].filename), "btest.c") != 0)
133    {
134      fprintf (stderr, "%s: [%d]: got %s expected test.c\n", name, index,
135	       all[index].filename);
136      *failed = 1;
137    }
138  if (all[index].lineno != want_lineno)
139    {
140      fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index,
141	       all[index].lineno, want_lineno);
142      *failed = 1;
143    }
144  if (strcmp (all[index].function, want_function) != 0)
145    {
146      fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index,
147	       all[index].function, want_function);
148      *failed = 1;
149    }
150}
151
152/* The backtrace callback function.  */
153
154static int
155callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
156	      const char *filename, int lineno, const char *function)
157{
158  struct bdata *data = (struct bdata *) vdata;
159  struct info *p;
160
161  if (data->index >= data->max)
162    {
163      fprintf (stderr, "callback_one: callback called too many times\n");
164      data->failed = 1;
165      return 1;
166    }
167
168  p = &data->all[data->index];
169  if (filename == NULL)
170    p->filename = NULL;
171  else
172    {
173      p->filename = strdup (filename);
174      assert (p->filename != NULL);
175    }
176  p->lineno = lineno;
177  if (function == NULL)
178    p->function = NULL;
179  else
180    {
181      p->function = strdup (function);
182      assert (p->function != NULL);
183    }
184  ++data->index;
185
186  return 0;
187}
188
189/* An error callback passed to backtrace.  */
190
191static void
192error_callback_one (void *vdata, const char *msg, int errnum)
193{
194  struct bdata *data = (struct bdata *) vdata;
195
196  fprintf (stderr, "%s", msg);
197  if (errnum > 0)
198    fprintf (stderr, ": %s", strerror (errnum));
199  fprintf (stderr, "\n");
200  data->failed = 1;
201}
202
203/* The backtrace_simple callback function.  */
204
205static int
206callback_two (void *vdata, uintptr_t pc)
207{
208  struct sdata *data = (struct sdata *) vdata;
209
210  if (data->index >= data->max)
211    {
212      fprintf (stderr, "callback_two: callback called too many times\n");
213      data->failed = 1;
214      return 1;
215    }
216
217  data->addrs[data->index] = pc;
218  ++data->index;
219
220  return 0;
221}
222
223/* An error callback passed to backtrace_simple.  */
224
225static void
226error_callback_two (void *vdata, const char *msg, int errnum)
227{
228  struct sdata *data = (struct sdata *) vdata;
229
230  fprintf (stderr, "%s", msg);
231  if (errnum > 0)
232    fprintf (stderr, ": %s", strerror (errnum));
233  fprintf (stderr, "\n");
234  data->failed = 1;
235}
236
237/* The backtrace_syminfo callback function.  */
238
239static void
240callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
241		const char *symname, uintptr_t symval)
242{
243  struct symdata *data = (struct symdata *) vdata;
244
245  if (symname == NULL)
246    data->name = NULL;
247  else
248    {
249      data->name = strdup (symname);
250      assert (data->name != NULL);
251    }
252  data->val = symval;
253}
254
255/* The backtrace_syminfo error callback function.  */
256
257static void
258error_callback_three (void *vdata, const char *msg, int errnum)
259{
260  struct symdata *data = (struct symdata *) vdata;
261
262  fprintf (stderr, "%s", msg);
263  if (errnum > 0)
264    fprintf (stderr, ": %s", strerror (errnum));
265  fprintf (stderr, "\n");
266  data->failed = 1;
267}
268
269/* Test the backtrace function with non-inlined functions.  */
270
271static int test1 (void) __attribute__ ((noinline, unused));
272static int f2 (int) __attribute__ ((noinline));
273static int f3 (int, int) __attribute__ ((noinline));
274
275static int
276test1 (void)
277{
278  /* Returning a value here and elsewhere avoids a tailcall which
279     would mess up the backtrace.  */
280  return f2 (__LINE__) + 1;
281}
282
283static int
284f2 (int f1line)
285{
286  return f3 (f1line, __LINE__) + 2;
287}
288
289static int
290f3 (int f1line, int f2line)
291{
292  struct info all[20];
293  struct bdata data;
294  int f3line;
295  int i;
296
297  data.all = &all[0];
298  data.index = 0;
299  data.max = 20;
300  data.failed = 0;
301
302  f3line = __LINE__ + 1;
303  i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
304
305  if (i != 0)
306    {
307      fprintf (stderr, "test1: unexpected return value %d\n", i);
308      data.failed = 1;
309    }
310
311  check ("test1", 0, all, f3line, "f3", &data.failed);
312  check ("test1", 1, all, f2line, "f2", &data.failed);
313  check ("test1", 2, all, f1line, "test1", &data.failed);
314
315  printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");
316
317  if (data.failed)
318    ++failures;
319
320  return failures;
321}
322
323/* Test the backtrace function with inlined functions.  */
324
325static inline int test2 (void) __attribute__ ((always_inline, unused));
326static inline int f12 (int) __attribute__ ((always_inline));
327static inline int f13 (int, int) __attribute__ ((always_inline));
328
329static inline int
330test2 (void)
331{
332  return f12 (__LINE__) + 1;
333}
334
335static inline int
336f12 (int f1line)
337{
338  return f13 (f1line, __LINE__) + 2;
339}
340
341static inline int
342f13 (int f1line, int f2line)
343{
344  struct info all[20];
345  struct bdata data;
346  int f3line;
347  int i;
348
349  data.all = &all[0];
350  data.index = 0;
351  data.max = 20;
352  data.failed = 0;
353
354  f3line = __LINE__ + 1;
355  i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
356
357  if (i != 0)
358    {
359      fprintf (stderr, "test2: unexpected return value %d\n", i);
360      data.failed = 1;
361    }
362
363  check ("test2", 0, all, f3line, "f13", &data.failed);
364  check ("test2", 1, all, f2line, "f12", &data.failed);
365  check ("test2", 2, all, f1line, "test2", &data.failed);
366
367  printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS");
368
369  if (data.failed)
370    ++failures;
371
372  return failures;
373}
374
375/* Test the backtrace_simple function with non-inlined functions.  */
376
377static int test3 (void) __attribute__ ((noinline, unused));
378static int f22 (int) __attribute__ ((noinline));
379static int f23 (int, int) __attribute__ ((noinline));
380
381static int
382test3 (void)
383{
384  return f22 (__LINE__) + 1;
385}
386
387static int
388f22 (int f1line)
389{
390  return f23 (f1line, __LINE__) + 2;
391}
392
393static int
394f23 (int f1line, int f2line)
395{
396  uintptr_t addrs[20];
397  struct sdata data;
398  int f3line;
399  int i;
400
401  data.addrs = &addrs[0];
402  data.index = 0;
403  data.max = 20;
404  data.failed = 0;
405
406  f3line = __LINE__ + 1;
407  i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
408
409  if (i != 0)
410    {
411      fprintf (stderr, "test3: unexpected return value %d\n", i);
412      data.failed = 1;
413    }
414
415  if (!data.failed)
416    {
417      struct info all[20];
418      struct bdata bdata;
419      int j;
420
421      bdata.all = &all[0];
422      bdata.index = 0;
423      bdata.max = 20;
424      bdata.failed = 0;
425
426      for (j = 0; j < 3; ++j)
427	{
428	  i = backtrace_pcinfo (state, addrs[j], callback_one,
429				error_callback_one, &bdata);
430	  if (i != 0)
431	    {
432	      fprintf (stderr,
433		       ("test3: unexpected return value "
434			"from backtrace_pcinfo %d\n"),
435		       i);
436	      bdata.failed = 1;
437	    }
438	  if (!bdata.failed && bdata.index != (size_t) (j + 1))
439	    {
440	      fprintf (stderr,
441		       ("wrong number of calls from backtrace_pcinfo "
442			"got %u expected %d\n"),
443		       (unsigned int) bdata.index, j + 1);
444	      bdata.failed = 1;
445	    }
446	}
447
448      check ("test3", 0, all, f3line, "f23", &bdata.failed);
449      check ("test3", 1, all, f2line, "f22", &bdata.failed);
450      check ("test3", 2, all, f1line, "test3", &bdata.failed);
451
452      if (bdata.failed)
453	data.failed = 1;
454
455      for (j = 0; j < 3; ++j)
456	{
457	  struct symdata symdata;
458
459	  symdata.name = NULL;
460	  symdata.val = 0;
461	  symdata.failed = 0;
462
463	  i = backtrace_syminfo (state, addrs[j], callback_three,
464				 error_callback_three, &symdata);
465	  if (i == 0)
466	    {
467	      fprintf (stderr,
468		       ("test3: [%d]: unexpected return value "
469			"from backtrace_syminfo %d\n"),
470		       j, i);
471	      symdata.failed = 1;
472	    }
473
474	  if (!symdata.failed)
475	    {
476	      const char *expected;
477
478	      switch (j)
479		{
480		case 0:
481		  expected = "f23";
482		  break;
483		case 1:
484		  expected = "f22";
485		  break;
486		case 2:
487		  expected = "test3";
488		  break;
489		default:
490		  assert (0);
491		}
492
493	      if (symdata.name == NULL)
494		{
495		  fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j);
496		  symdata.failed = 1;
497		}
498	      /* Use strncmp, not strcmp, because GCC might create a
499		 clone.  */
500	      else if (strncmp (symdata.name, expected, strlen (expected))
501		       != 0)
502		{
503		  fprintf (stderr,
504			   ("test3: [%d]: unexpected syminfo name "
505			    "got %s expected %s\n"),
506			   j, symdata.name, expected);
507		  symdata.failed = 1;
508		}
509	    }
510
511	  if (symdata.failed)
512	    data.failed = 1;
513	}
514    }
515
516  printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS");
517
518  if (data.failed)
519    ++failures;
520
521  return failures;
522}
523
524/* Test the backtrace_simple function with inlined functions.  */
525
526static inline int test4 (void) __attribute__ ((always_inline, unused));
527static inline int f32 (int) __attribute__ ((always_inline));
528static inline int f33 (int, int) __attribute__ ((always_inline));
529
530static inline int
531test4 (void)
532{
533  return f32 (__LINE__) + 1;
534}
535
536static inline int
537f32 (int f1line)
538{
539  return f33 (f1line, __LINE__) + 2;
540}
541
542static inline int
543f33 (int f1line, int f2line)
544{
545  uintptr_t addrs[20];
546  struct sdata data;
547  int f3line;
548  int i;
549
550  data.addrs = &addrs[0];
551  data.index = 0;
552  data.max = 20;
553  data.failed = 0;
554
555  f3line = __LINE__ + 1;
556  i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
557
558  if (i != 0)
559    {
560      fprintf (stderr, "test3: unexpected return value %d\n", i);
561      data.failed = 1;
562    }
563
564  if (!data.failed)
565    {
566      struct info all[20];
567      struct bdata bdata;
568
569      bdata.all = &all[0];
570      bdata.index = 0;
571      bdata.max = 20;
572      bdata.failed = 0;
573
574      i = backtrace_pcinfo (state, addrs[0], callback_one, error_callback_one,
575			    &bdata);
576      if (i != 0)
577	{
578	  fprintf (stderr,
579		   ("test4: unexpected return value "
580		    "from backtrace_pcinfo %d\n"),
581		   i);
582	  bdata.failed = 1;
583	}
584
585      check ("test4", 0, all, f3line, "f33", &bdata.failed);
586      check ("test4", 1, all, f2line, "f32", &bdata.failed);
587      check ("test4", 2, all, f1line, "test4", &bdata.failed);
588
589      if (bdata.failed)
590	data.failed = 1;
591    }
592
593  printf ("%s: backtrace_simple inline\n", data.failed ? "FAIL" : "PASS");
594
595  if (data.failed)
596    ++failures;
597
598  return failures;
599}
600
601static void
602error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg,
603		       int errnum)
604{
605  fprintf (stderr, "%s", msg);
606  if (errnum > 0)
607    fprintf (stderr, ": %s", strerror (errnum));
608  fprintf (stderr, "\n");
609  exit (EXIT_FAILURE);
610}
611
612/* Run all the tests.  */
613
614int
615main (int argc ATTRIBUTE_UNUSED, char **argv)
616{
617  state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS,
618				  error_callback_create, NULL);
619
620#if BACKTRACE_SUPPORTED
621  test1 ();
622  test2 ();
623  test3 ();
624  test4 ();
625#endif
626
627  exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
628}
629