1/* btest.c -- Test for libbacktrace library
2   Copyright (C) 2012-2015 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, size;
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 (all[index].filename == NULL || all[index].function == NULL)
133    {
134      fprintf (stderr, "%s: [%d]: missing file name or function name\n",
135	       name, index);
136      *failed = 1;
137      return;
138    }
139  if (strcmp (base (all[index].filename), "btest.c") != 0)
140    {
141      fprintf (stderr, "%s: [%d]: got %s expected test.c\n", name, index,
142	       all[index].filename);
143      *failed = 1;
144    }
145  if (all[index].lineno != want_lineno)
146    {
147      fprintf (stderr, "%s: [%d]: got %d expected %d\n", name, index,
148	       all[index].lineno, want_lineno);
149      *failed = 1;
150    }
151  if (strcmp (all[index].function, want_function) != 0)
152    {
153      fprintf (stderr, "%s: [%d]: got %s expected %s\n", name, index,
154	       all[index].function, want_function);
155      *failed = 1;
156    }
157}
158
159/* The backtrace callback function.  */
160
161static int
162callback_one (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
163	      const char *filename, int lineno, const char *function)
164{
165  struct bdata *data = (struct bdata *) vdata;
166  struct info *p;
167
168  if (data->index >= data->max)
169    {
170      fprintf (stderr, "callback_one: callback called too many times\n");
171      data->failed = 1;
172      return 1;
173    }
174
175  p = &data->all[data->index];
176  if (filename == NULL)
177    p->filename = NULL;
178  else
179    {
180      p->filename = strdup (filename);
181      assert (p->filename != NULL);
182    }
183  p->lineno = lineno;
184  if (function == NULL)
185    p->function = NULL;
186  else
187    {
188      p->function = strdup (function);
189      assert (p->function != NULL);
190    }
191  ++data->index;
192
193  return 0;
194}
195
196/* An error callback passed to backtrace.  */
197
198static void
199error_callback_one (void *vdata, const char *msg, int errnum)
200{
201  struct bdata *data = (struct bdata *) vdata;
202
203  fprintf (stderr, "%s", msg);
204  if (errnum > 0)
205    fprintf (stderr, ": %s", strerror (errnum));
206  fprintf (stderr, "\n");
207  data->failed = 1;
208}
209
210/* The backtrace_simple callback function.  */
211
212static int
213callback_two (void *vdata, uintptr_t pc)
214{
215  struct sdata *data = (struct sdata *) vdata;
216
217  if (data->index >= data->max)
218    {
219      fprintf (stderr, "callback_two: callback called too many times\n");
220      data->failed = 1;
221      return 1;
222    }
223
224  data->addrs[data->index] = pc;
225  ++data->index;
226
227  return 0;
228}
229
230/* An error callback passed to backtrace_simple.  */
231
232static void
233error_callback_two (void *vdata, const char *msg, int errnum)
234{
235  struct sdata *data = (struct sdata *) vdata;
236
237  fprintf (stderr, "%s", msg);
238  if (errnum > 0)
239    fprintf (stderr, ": %s", strerror (errnum));
240  fprintf (stderr, "\n");
241  data->failed = 1;
242}
243
244/* The backtrace_syminfo callback function.  */
245
246static void
247callback_three (void *vdata, uintptr_t pc ATTRIBUTE_UNUSED,
248		const char *symname, uintptr_t symval,
249		uintptr_t symsize)
250{
251  struct symdata *data = (struct symdata *) vdata;
252
253  if (symname == NULL)
254    data->name = NULL;
255  else
256    {
257      data->name = strdup (symname);
258      assert (data->name != NULL);
259    }
260  data->val = symval;
261  data->size = symsize;
262}
263
264/* The backtrace_syminfo error callback function.  */
265
266static void
267error_callback_three (void *vdata, const char *msg, int errnum)
268{
269  struct symdata *data = (struct symdata *) vdata;
270
271  fprintf (stderr, "%s", msg);
272  if (errnum > 0)
273    fprintf (stderr, ": %s", strerror (errnum));
274  fprintf (stderr, "\n");
275  data->failed = 1;
276}
277
278/* Test the backtrace function with non-inlined functions.  */
279
280static int test1 (void) __attribute__ ((noinline, unused));
281static int f2 (int) __attribute__ ((noinline));
282static int f3 (int, int) __attribute__ ((noinline));
283
284static int
285test1 (void)
286{
287  /* Returning a value here and elsewhere avoids a tailcall which
288     would mess up the backtrace.  */
289  return f2 (__LINE__) + 1;
290}
291
292static int
293f2 (int f1line)
294{
295  return f3 (f1line, __LINE__) + 2;
296}
297
298static int
299f3 (int f1line, int f2line)
300{
301  struct info all[20];
302  struct bdata data;
303  int f3line;
304  int i;
305
306  data.all = &all[0];
307  data.index = 0;
308  data.max = 20;
309  data.failed = 0;
310
311  f3line = __LINE__ + 1;
312  i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
313
314  if (i != 0)
315    {
316      fprintf (stderr, "test1: unexpected return value %d\n", i);
317      data.failed = 1;
318    }
319
320  if (data.index < 3)
321    {
322      fprintf (stderr,
323	       "test1: not enough frames; got %zu, expected at least 3\n",
324	       data.index);
325      data.failed = 1;
326    }
327
328  check ("test1", 0, all, f3line, "f3", &data.failed);
329  check ("test1", 1, all, f2line, "f2", &data.failed);
330  check ("test1", 2, all, f1line, "test1", &data.failed);
331
332  printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");
333
334  if (data.failed)
335    ++failures;
336
337  return failures;
338}
339
340/* Test the backtrace function with inlined functions.  */
341
342static inline int test2 (void) __attribute__ ((always_inline, unused));
343static inline int f12 (int) __attribute__ ((always_inline));
344static inline int f13 (int, int) __attribute__ ((always_inline));
345
346static inline int
347test2 (void)
348{
349  return f12 (__LINE__) + 1;
350}
351
352static inline int
353f12 (int f1line)
354{
355  return f13 (f1line, __LINE__) + 2;
356}
357
358static inline int
359f13 (int f1line, int f2line)
360{
361  struct info all[20];
362  struct bdata data;
363  int f3line;
364  int i;
365
366  data.all = &all[0];
367  data.index = 0;
368  data.max = 20;
369  data.failed = 0;
370
371  f3line = __LINE__ + 1;
372  i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
373
374  if (i != 0)
375    {
376      fprintf (stderr, "test2: unexpected return value %d\n", i);
377      data.failed = 1;
378    }
379
380  check ("test2", 0, all, f3line, "f13", &data.failed);
381  check ("test2", 1, all, f2line, "f12", &data.failed);
382  check ("test2", 2, all, f1line, "test2", &data.failed);
383
384  printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS");
385
386  if (data.failed)
387    ++failures;
388
389  return failures;
390}
391
392/* Test the backtrace_simple function with non-inlined functions.  */
393
394static int test3 (void) __attribute__ ((noinline, unused));
395static int f22 (int) __attribute__ ((noinline));
396static int f23 (int, int) __attribute__ ((noinline));
397
398static int
399test3 (void)
400{
401  return f22 (__LINE__) + 1;
402}
403
404static int
405f22 (int f1line)
406{
407  return f23 (f1line, __LINE__) + 2;
408}
409
410static int
411f23 (int f1line, int f2line)
412{
413  uintptr_t addrs[20];
414  struct sdata data;
415  int f3line;
416  int i;
417
418  data.addrs = &addrs[0];
419  data.index = 0;
420  data.max = 20;
421  data.failed = 0;
422
423  f3line = __LINE__ + 1;
424  i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
425
426  if (i != 0)
427    {
428      fprintf (stderr, "test3: unexpected return value %d\n", i);
429      data.failed = 1;
430    }
431
432  if (!data.failed)
433    {
434      struct info all[20];
435      struct bdata bdata;
436      int j;
437
438      bdata.all = &all[0];
439      bdata.index = 0;
440      bdata.max = 20;
441      bdata.failed = 0;
442
443      for (j = 0; j < 3; ++j)
444	{
445	  i = backtrace_pcinfo (state, addrs[j], callback_one,
446				error_callback_one, &bdata);
447	  if (i != 0)
448	    {
449	      fprintf (stderr,
450		       ("test3: unexpected return value "
451			"from backtrace_pcinfo %d\n"),
452		       i);
453	      bdata.failed = 1;
454	    }
455	  if (!bdata.failed && bdata.index != (size_t) (j + 1))
456	    {
457	      fprintf (stderr,
458		       ("wrong number of calls from backtrace_pcinfo "
459			"got %u expected %d\n"),
460		       (unsigned int) bdata.index, j + 1);
461	      bdata.failed = 1;
462	    }
463	}
464
465      check ("test3", 0, all, f3line, "f23", &bdata.failed);
466      check ("test3", 1, all, f2line, "f22", &bdata.failed);
467      check ("test3", 2, all, f1line, "test3", &bdata.failed);
468
469      if (bdata.failed)
470	data.failed = 1;
471
472      for (j = 0; j < 3; ++j)
473	{
474	  struct symdata symdata;
475
476	  symdata.name = NULL;
477	  symdata.val = 0;
478	  symdata.size = 0;
479	  symdata.failed = 0;
480
481	  i = backtrace_syminfo (state, addrs[j], callback_three,
482				 error_callback_three, &symdata);
483	  if (i == 0)
484	    {
485	      fprintf (stderr,
486		       ("test3: [%d]: unexpected return value "
487			"from backtrace_syminfo %d\n"),
488		       j, i);
489	      symdata.failed = 1;
490	    }
491
492	  if (!symdata.failed)
493	    {
494	      const char *expected;
495
496	      switch (j)
497		{
498		case 0:
499		  expected = "f23";
500		  break;
501		case 1:
502		  expected = "f22";
503		  break;
504		case 2:
505		  expected = "test3";
506		  break;
507		default:
508		  assert (0);
509		}
510
511	      if (symdata.name == NULL)
512		{
513		  fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j);
514		  symdata.failed = 1;
515		}
516	      /* Use strncmp, not strcmp, because GCC might create a
517		 clone.  */
518	      else if (strncmp (symdata.name, expected, strlen (expected))
519		       != 0)
520		{
521		  fprintf (stderr,
522			   ("test3: [%d]: unexpected syminfo name "
523			    "got %s expected %s\n"),
524			   j, symdata.name, expected);
525		  symdata.failed = 1;
526		}
527	    }
528
529	  if (symdata.failed)
530	    data.failed = 1;
531	}
532    }
533
534  printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS");
535
536  if (data.failed)
537    ++failures;
538
539  return failures;
540}
541
542/* Test the backtrace_simple function with inlined functions.  */
543
544static inline int test4 (void) __attribute__ ((always_inline, unused));
545static inline int f32 (int) __attribute__ ((always_inline));
546static inline int f33 (int, int) __attribute__ ((always_inline));
547
548static inline int
549test4 (void)
550{
551  return f32 (__LINE__) + 1;
552}
553
554static inline int
555f32 (int f1line)
556{
557  return f33 (f1line, __LINE__) + 2;
558}
559
560static inline int
561f33 (int f1line, int f2line)
562{
563  uintptr_t addrs[20];
564  struct sdata data;
565  int f3line;
566  int i;
567
568  data.addrs = &addrs[0];
569  data.index = 0;
570  data.max = 20;
571  data.failed = 0;
572
573  f3line = __LINE__ + 1;
574  i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
575
576  if (i != 0)
577    {
578      fprintf (stderr, "test3: unexpected return value %d\n", i);
579      data.failed = 1;
580    }
581
582  if (!data.failed)
583    {
584      struct info all[20];
585      struct bdata bdata;
586
587      bdata.all = &all[0];
588      bdata.index = 0;
589      bdata.max = 20;
590      bdata.failed = 0;
591
592      i = backtrace_pcinfo (state, addrs[0], callback_one, error_callback_one,
593			    &bdata);
594      if (i != 0)
595	{
596	  fprintf (stderr,
597		   ("test4: unexpected return value "
598		    "from backtrace_pcinfo %d\n"),
599		   i);
600	  bdata.failed = 1;
601	}
602
603      check ("test4", 0, all, f3line, "f33", &bdata.failed);
604      check ("test4", 1, all, f2line, "f32", &bdata.failed);
605      check ("test4", 2, all, f1line, "test4", &bdata.failed);
606
607      if (bdata.failed)
608	data.failed = 1;
609    }
610
611  printf ("%s: backtrace_simple inline\n", data.failed ? "FAIL" : "PASS");
612
613  if (data.failed)
614    ++failures;
615
616  return failures;
617}
618
619int global = 1;
620
621static int
622test5 (void)
623{
624  struct symdata symdata;
625  int i;
626  uintptr_t addr = (uintptr_t) &global;
627
628  if (sizeof (global) > 1)
629    addr += 1;
630
631  symdata.name = NULL;
632  symdata.val = 0;
633  symdata.size = 0;
634  symdata.failed = 0;
635
636  i = backtrace_syminfo (state, addr, callback_three,
637			 error_callback_three, &symdata);
638  if (i == 0)
639    {
640      fprintf (stderr,
641	       "test5: unexpected return value from backtrace_syminfo %d\n",
642	       i);
643      symdata.failed = 1;
644    }
645
646  if (!symdata.failed)
647    {
648      if (symdata.name == NULL)
649	{
650	  fprintf (stderr, "test5: NULL syminfo name\n");
651	  symdata.failed = 1;
652	}
653      else if (strcmp (symdata.name, "global") != 0)
654	{
655	  fprintf (stderr,
656		   "test5: unexpected syminfo name got %s expected %s\n",
657		   symdata.name, "global");
658	  symdata.failed = 1;
659	}
660      else if (symdata.val != (uintptr_t) &global)
661	{
662	  fprintf (stderr,
663		   "test5: unexpected syminfo value got %lx expected %lx\n",
664		   (unsigned long) symdata.val,
665		   (unsigned long) (uintptr_t) &global);
666	  symdata.failed = 1;
667	}
668      else if (symdata.size != sizeof (global))
669	{
670	  fprintf (stderr,
671		   "test5: unexpected syminfo size got %lx expected %lx\n",
672		   (unsigned long) symdata.size,
673		   (unsigned long) sizeof (global));
674	  symdata.failed = 1;
675	}
676    }
677
678  printf ("%s: backtrace_syminfo variable\n",
679	  symdata.failed ? "FAIL" : "PASS");
680
681  if (symdata.failed)
682    ++failures;
683
684  return failures;
685}
686
687static void
688error_callback_create (void *data ATTRIBUTE_UNUSED, const char *msg,
689		       int errnum)
690{
691  fprintf (stderr, "%s", msg);
692  if (errnum > 0)
693    fprintf (stderr, ": %s", strerror (errnum));
694  fprintf (stderr, "\n");
695  exit (EXIT_FAILURE);
696}
697
698/* Run all the tests.  */
699
700int
701main (int argc ATTRIBUTE_UNUSED, char **argv)
702{
703  state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS,
704				  error_callback_create, NULL);
705
706#if BACKTRACE_SUPPORTED
707  test1 ();
708  test2 ();
709  test3 ();
710  test4 ();
711  test5 ();
712#endif
713
714  exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
715}
716