1/* btest.c -- Test for libbacktrace library
2   Copyright (C) 2012-2022 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#include <unistd.h>
41#include <sys/stat.h>
42
43#include "filenames.h"
44
45#include "backtrace.h"
46#include "backtrace-supported.h"
47
48#include "testlib.h"
49
50/* Test the backtrace function with non-inlined functions.  */
51
52static int test1 (void) __attribute__ ((noinline, noclone, unused));
53static int f2 (int) __attribute__ ((noinline, noclone));
54static int f3 (int, int) __attribute__ ((noinline, noclone));
55
56static int
57test1 (void)
58{
59  /* Returning a value here and elsewhere avoids a tailcall which
60     would mess up the backtrace.  */
61  return f2 (__LINE__) + 1;
62}
63
64static int
65f2 (int f1line)
66{
67  return f3 (f1line, __LINE__) + 2;
68}
69
70static int
71f3 (int f1line, int f2line)
72{
73  struct info all[20];
74  struct bdata data;
75  int f3line;
76  int i;
77
78  data.all = &all[0];
79  data.index = 0;
80  data.max = 20;
81  data.failed = 0;
82
83  f3line = __LINE__ + 1;
84  i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
85
86  if (i != 0)
87    {
88      fprintf (stderr, "test1: unexpected return value %d\n", i);
89      data.failed = 1;
90    }
91
92  if (data.index < 3)
93    {
94      fprintf (stderr,
95	       "test1: not enough frames; got %zu, expected at least 3\n",
96	       data.index);
97      data.failed = 1;
98    }
99
100  check ("test1", 0, all, f3line, "f3", "btest.c", &data.failed);
101  check ("test1", 1, all, f2line, "f2", "btest.c", &data.failed);
102  check ("test1", 2, all, f1line, "test1", "btest.c", &data.failed);
103
104  printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS");
105
106  if (data.failed)
107    ++failures;
108
109  return failures;
110}
111
112/* Test the backtrace function with inlined functions.  */
113
114static inline int test2 (void) __attribute__ ((always_inline, unused));
115static inline int f12 (int) __attribute__ ((always_inline));
116static inline int f13 (int, int) __attribute__ ((always_inline));
117
118static inline int
119test2 (void)
120{
121  return f12 (__LINE__) + 1;
122}
123
124static inline int
125f12 (int f1line)
126{
127  return f13 (f1line, __LINE__) + 2;
128}
129
130static inline int
131f13 (int f1line, int f2line)
132{
133  struct info all[20];
134  struct bdata data;
135  int f3line;
136  int i;
137
138  data.all = &all[0];
139  data.index = 0;
140  data.max = 20;
141  data.failed = 0;
142
143  f3line = __LINE__ + 1;
144  i = backtrace_full (state, 0, callback_one, error_callback_one, &data);
145
146  if (i != 0)
147    {
148      fprintf (stderr, "test2: unexpected return value %d\n", i);
149      data.failed = 1;
150    }
151
152  check ("test2", 0, all, f3line, "f13", "btest.c", &data.failed);
153  check ("test2", 1, all, f2line, "f12", "btest.c", &data.failed);
154  check ("test2", 2, all, f1line, "test2", "btest.c", &data.failed);
155
156  printf ("%s: backtrace_full inline\n", data.failed ? "FAIL" : "PASS");
157
158  if (data.failed)
159    ++failures;
160
161  return failures;
162}
163
164/* Test the backtrace_simple function with non-inlined functions.  */
165
166static int test3 (void) __attribute__ ((noinline, noclone, unused));
167static int f22 (int) __attribute__ ((noinline, noclone));
168static int f23 (int, int) __attribute__ ((noinline, noclone));
169
170static int
171test3 (void)
172{
173  return f22 (__LINE__) + 1;
174}
175
176static int
177f22 (int f1line)
178{
179  return f23 (f1line, __LINE__) + 2;
180}
181
182static int
183f23 (int f1line, int f2line)
184{
185  uintptr_t addrs[20];
186  struct sdata data;
187  int f3line;
188  int i;
189
190  data.addrs = &addrs[0];
191  data.index = 0;
192  data.max = 20;
193  data.failed = 0;
194
195  f3line = __LINE__ + 1;
196  i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
197
198  if (i != 0)
199    {
200      fprintf (stderr, "test3: unexpected return value %d\n", i);
201      data.failed = 1;
202    }
203
204  if (!data.failed)
205    {
206      struct info all[20];
207      struct bdata bdata;
208      int j;
209
210      bdata.all = &all[0];
211      bdata.index = 0;
212      bdata.max = 20;
213      bdata.failed = 0;
214
215      for (j = 0; j < 3; ++j)
216	{
217	  i = backtrace_pcinfo (state, addrs[j], callback_one,
218				error_callback_one, &bdata);
219	  if (i != 0)
220	    {
221	      fprintf (stderr,
222		       ("test3: unexpected return value "
223			"from backtrace_pcinfo %d\n"),
224		       i);
225	      bdata.failed = 1;
226	    }
227	  if (!bdata.failed && bdata.index != (size_t) (j + 1))
228	    {
229	      fprintf (stderr,
230		       ("wrong number of calls from backtrace_pcinfo "
231			"got %u expected %d\n"),
232		       (unsigned int) bdata.index, j + 1);
233	      bdata.failed = 1;
234	    }
235	}
236
237      check ("test3", 0, all, f3line, "f23", "btest.c", &bdata.failed);
238      check ("test3", 1, all, f2line, "f22", "btest.c", &bdata.failed);
239      check ("test3", 2, all, f1line, "test3", "btest.c", &bdata.failed);
240
241      if (bdata.failed)
242	data.failed = 1;
243
244      for (j = 0; j < 3; ++j)
245	{
246	  struct symdata symdata;
247
248	  symdata.name = NULL;
249	  symdata.val = 0;
250	  symdata.size = 0;
251	  symdata.failed = 0;
252
253	  i = backtrace_syminfo (state, addrs[j], callback_three,
254				 error_callback_three, &symdata);
255	  if (i == 0)
256	    {
257	      fprintf (stderr,
258		       ("test3: [%d]: unexpected return value "
259			"from backtrace_syminfo %d\n"),
260		       j, i);
261	      symdata.failed = 1;
262	    }
263
264	  if (!symdata.failed)
265	    {
266	      const char *expected;
267
268	      switch (j)
269		{
270		case 0:
271		  expected = "f23";
272		  break;
273		case 1:
274		  expected = "f22";
275		  break;
276		case 2:
277		  expected = "test3";
278		  break;
279		default:
280		  assert (0);
281		}
282
283	      if (symdata.name == NULL)
284		{
285		  fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j);
286		  symdata.failed = 1;
287		}
288	      /* Use strncmp, not strcmp, because GCC might create a
289		 clone.  */
290	      else if (strncmp (symdata.name, expected, strlen (expected))
291		       != 0)
292		{
293		  fprintf (stderr,
294			   ("test3: [%d]: unexpected syminfo name "
295			    "got %s expected %s\n"),
296			   j, symdata.name, expected);
297		  symdata.failed = 1;
298		}
299	    }
300
301	  if (symdata.failed)
302	    data.failed = 1;
303	}
304    }
305
306  printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS");
307
308  if (data.failed)
309    ++failures;
310
311  return failures;
312}
313
314/* Test the backtrace_simple function with inlined functions.  */
315
316static inline int test4 (void) __attribute__ ((always_inline, unused));
317static inline int f32 (int) __attribute__ ((always_inline));
318static inline int f33 (int, int) __attribute__ ((always_inline));
319
320static inline int
321test4 (void)
322{
323  return f32 (__LINE__) + 1;
324}
325
326static inline int
327f32 (int f1line)
328{
329  return f33 (f1line, __LINE__) + 2;
330}
331
332static inline int
333f33 (int f1line, int f2line)
334{
335  uintptr_t addrs[20];
336  struct sdata data;
337  int f3line;
338  int i;
339
340  data.addrs = &addrs[0];
341  data.index = 0;
342  data.max = 20;
343  data.failed = 0;
344
345  f3line = __LINE__ + 1;
346  i = backtrace_simple (state, 0, callback_two, error_callback_two, &data);
347
348  if (i != 0)
349    {
350      fprintf (stderr, "test3: unexpected return value %d\n", i);
351      data.failed = 1;
352    }
353
354  if (!data.failed)
355    {
356      struct info all[20];
357      struct bdata bdata;
358
359      bdata.all = &all[0];
360      bdata.index = 0;
361      bdata.max = 20;
362      bdata.failed = 0;
363
364      i = backtrace_pcinfo (state, addrs[0], callback_one, error_callback_one,
365			    &bdata);
366      if (i != 0)
367	{
368	  fprintf (stderr,
369		   ("test4: unexpected return value "
370		    "from backtrace_pcinfo %d\n"),
371		   i);
372	  bdata.failed = 1;
373	}
374
375      check ("test4", 0, all, f3line, "f33", "btest.c", &bdata.failed);
376      check ("test4", 1, all, f2line, "f32", "btest.c", &bdata.failed);
377      check ("test4", 2, all, f1line, "test4", "btest.c", &bdata.failed);
378
379      if (bdata.failed)
380	data.failed = 1;
381    }
382
383  printf ("%s: backtrace_simple inline\n", data.failed ? "FAIL" : "PASS");
384
385  if (data.failed)
386    ++failures;
387
388  return failures;
389}
390
391static int test5 (void) __attribute__ ((unused));
392
393int global = 1;
394
395static int
396test5 (void)
397{
398  struct symdata symdata;
399  int i;
400  uintptr_t addr = (uintptr_t) &global;
401
402  if (sizeof (global) > 1)
403    addr += 1;
404
405  symdata.name = NULL;
406  symdata.val = 0;
407  symdata.size = 0;
408  symdata.failed = 0;
409
410  i = backtrace_syminfo (state, addr, callback_three,
411			 error_callback_three, &symdata);
412  if (i == 0)
413    {
414      fprintf (stderr,
415	       "test5: unexpected return value from backtrace_syminfo %d\n",
416	       i);
417      symdata.failed = 1;
418    }
419
420  if (!symdata.failed)
421    {
422      if (symdata.name == NULL)
423	{
424	  fprintf (stderr, "test5: NULL syminfo name\n");
425	  symdata.failed = 1;
426	}
427      else if (!(strncmp (symdata.name, "global", 6) == 0
428		 && (symdata.name[6] == '\0'|| symdata.name[6] == '.')))
429	{
430	  fprintf (stderr,
431		   "test5: unexpected syminfo name got %s expected %s\n",
432		   symdata.name, "global");
433	  symdata.failed = 1;
434	}
435      else if (symdata.val != (uintptr_t) &global)
436	{
437	  fprintf (stderr,
438		   "test5: unexpected syminfo value got %lx expected %lx\n",
439		   (unsigned long) symdata.val,
440		   (unsigned long) (uintptr_t) &global);
441	  symdata.failed = 1;
442	}
443      else if (symdata.size != sizeof (global))
444	{
445	  fprintf (stderr,
446		   "test5: unexpected syminfo size got %lx expected %lx\n",
447		   (unsigned long) symdata.size,
448		   (unsigned long) sizeof (global));
449	  symdata.failed = 1;
450	}
451    }
452
453  printf ("%s: backtrace_syminfo variable\n",
454	  symdata.failed ? "FAIL" : "PASS");
455
456  if (symdata.failed)
457    ++failures;
458
459  return failures;
460}
461
462#define MIN_DESCRIPTOR 3
463#define MAX_DESCRIPTOR 10
464
465static int fstat_status[MAX_DESCRIPTOR];
466
467/* Check files that are available.  */
468
469static void
470check_available_files (void)
471{
472  struct stat s;
473  for (unsigned i = MIN_DESCRIPTOR; i < MAX_DESCRIPTOR; i++)
474    fstat_status[i] = fstat (i, &s);
475}
476
477/* Check that are no files left open.  */
478
479static void
480check_open_files (void)
481{
482  for (unsigned i = MIN_DESCRIPTOR; i < MAX_DESCRIPTOR; i++)
483    {
484      if (fstat_status[i] != 0 && close (i) == 0)
485	{
486	  fprintf (stderr,
487		   "ERROR: descriptor %d still open after tests complete\n",
488		   i);
489	  ++failures;
490	}
491    }
492}
493
494/* Run all the tests.  */
495
496int
497main (int argc ATTRIBUTE_UNUSED, char **argv)
498{
499  check_available_files ();
500
501  state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS,
502				  error_callback_create, NULL);
503
504#if BACKTRACE_SUPPORTED
505  test1 ();
506  test2 ();
507  test3 ();
508  test4 ();
509#if BACKTRACE_SUPPORTS_DATA
510  test5 ();
511#endif
512#endif
513
514  check_open_files ();
515
516  exit (failures ? EXIT_FAILURE : EXIT_SUCCESS);
517}
518