1 /* btest.c -- Test for libbacktrace library
2 Copyright (C) 2012-2016 Free Software Foundation, Inc.
3 Written by Ian Lance Taylor, Google.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions are
7 met:
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
21 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
22 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24 DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
25 INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
26 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
29 STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
30 IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 POSSIBILITY 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
67 struct info
68 {
69 char *filename;
70 int lineno;
71 char *function;
72 };
73
74 /* Passed to backtrace callback function. */
75
76 struct 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
86 struct 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
96 struct symdata
97 {
98 const char *name;
99 uintptr_t val, size;
100 int failed;
101 };
102
103 /* The backtrace state. */
104
105 static void *state;
106
107 /* The number of failures. */
108
109 static int failures;
110
111 /* Return the base name in a path. */
112
113 static const char *
base(const char * p)114 base (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
130 static void
check(const char * name,int index,const struct info * all,int want_lineno,const char * want_function,int * failed)131 check (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
165 static int
callback_one(void * vdata,uintptr_t pc ATTRIBUTE_UNUSED,const char * filename,int lineno,const char * function)166 callback_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
202 static void
error_callback_one(void * vdata,const char * msg,int errnum)203 error_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
216 static int
callback_two(void * vdata,uintptr_t pc)217 callback_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
236 static void
error_callback_two(void * vdata,const char * msg,int errnum)237 error_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
250 static void
callback_three(void * vdata,uintptr_t pc ATTRIBUTE_UNUSED,const char * symname,uintptr_t symval,uintptr_t symsize)251 callback_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
270 static void
error_callback_three(void * vdata,const char * msg,int errnum)271 error_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
284 static int test1 (void) __attribute__ ((noinline, unused));
285 static int f2 (int) __attribute__ ((noinline));
286 static int f3 (int, int) __attribute__ ((noinline));
287
288 static int
test1(void)289 test1 (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
296 static int
f2(int f1line)297 f2 (int f1line)
298 {
299 return f3 (f1line, __LINE__) + 2;
300 }
301
302 static int
f3(int f1line,int f2line)303 f3 (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
346 static inline int test2 (void) __attribute__ ((always_inline, unused));
347 static inline int f12 (int) __attribute__ ((always_inline));
348 static inline int f13 (int, int) __attribute__ ((always_inline));
349
350 static inline int
test2(void)351 test2 (void)
352 {
353 return f12 (__LINE__) + 1;
354 }
355
356 static inline int
f12(int f1line)357 f12 (int f1line)
358 {
359 return f13 (f1line, __LINE__) + 2;
360 }
361
362 static inline int
f13(int f1line,int f2line)363 f13 (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
398 static int test3 (void) __attribute__ ((noinline, unused));
399 static int f22 (int) __attribute__ ((noinline));
400 static int f23 (int, int) __attribute__ ((noinline));
401
402 static int
test3(void)403 test3 (void)
404 {
405 return f22 (__LINE__) + 1;
406 }
407
408 static int
f22(int f1line)409 f22 (int f1line)
410 {
411 return f23 (f1line, __LINE__) + 2;
412 }
413
414 static int
f23(int f1line,int f2line)415 f23 (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
548 static inline int test4 (void) __attribute__ ((always_inline, unused));
549 static inline int f32 (int) __attribute__ ((always_inline));
550 static inline int f33 (int, int) __attribute__ ((always_inline));
551
552 static inline int
test4(void)553 test4 (void)
554 {
555 return f32 (__LINE__) + 1;
556 }
557
558 static inline int
f32(int f1line)559 f32 (int f1line)
560 {
561 return f33 (f1line, __LINE__) + 2;
562 }
563
564 static inline int
f33(int f1line,int f2line)565 f33 (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
625 int global = 1;
626
627 static int
test5(void)628 test5 (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
695 static void
error_callback_create(void * data ATTRIBUTE_UNUSED,const char * msg,int errnum)696 error_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
708 int
main(int argc ATTRIBUTE_UNUSED,char ** argv)709 main (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