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