]>
Commit | Line | Data |
---|---|---|
eff02e4f | 1 | /* btest.c -- Test for libbacktrace library |
f8a7e1a4 | 2 | Copyright (C) 2012-2013 Free Software Foundation, Inc. |
eff02e4f ILT |
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> | |
eff02e4f ILT |
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 | ||
63 | struct info | |
64 | { | |
65 | char *filename; | |
66 | int lineno; | |
67 | char *function; | |
68 | }; | |
69 | ||
70 | /* Passed to backtrace callback function. */ | |
71 | ||
72 | struct 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 | ||
82 | struct 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 | ||
92 | struct symdata | |
93 | { | |
94 | const char *name; | |
95 | uintptr_t val; | |
96 | int failed; | |
97 | }; | |
98 | ||
99 | /* The backtrace state. */ | |
100 | ||
101 | static void *state; | |
102 | ||
103 | /* The number of failures. */ | |
104 | ||
105 | static int failures; | |
106 | ||
107 | /* Return the base name in a path. */ | |
108 | ||
109 | static const char * | |
110 | base (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 | ||
126 | static void | |
127 | check (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 | ||
154 | static int | |
155 | callback_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 | ||
191 | static void | |
192 | error_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 | ||
205 | static int | |
206 | callback_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 | ||
225 | static void | |
226 | error_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 | ||
239 | static void | |
240 | callback_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 | ||
257 | static void | |
258 | error_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 | ||
bd3e497d | 271 | static int test1 (void) __attribute__ ((noinline, unused)); |
eff02e4f ILT |
272 | static int f2 (int) __attribute__ ((noinline)); |
273 | static int f3 (int, int) __attribute__ ((noinline)); | |
274 | ||
275 | static int | |
276 | test1 (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 | ||
283 | static int | |
284 | f2 (int f1line) | |
285 | { | |
286 | return f3 (f1line, __LINE__) + 2; | |
287 | } | |
288 | ||
289 | static int | |
290 | f3 (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 | ||
bd3e497d | 325 | static inline int test2 (void) __attribute__ ((always_inline, unused)); |
eff02e4f ILT |
326 | static inline int f12 (int) __attribute__ ((always_inline)); |
327 | static inline int f13 (int, int) __attribute__ ((always_inline)); | |
328 | ||
329 | static inline int | |
330 | test2 (void) | |
331 | { | |
332 | return f12 (__LINE__) + 1; | |
333 | } | |
334 | ||
335 | static inline int | |
336 | f12 (int f1line) | |
337 | { | |
338 | return f13 (f1line, __LINE__) + 2; | |
339 | } | |
340 | ||
341 | static inline int | |
342 | f13 (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 | ||
bd3e497d | 377 | static int test3 (void) __attribute__ ((noinline, unused)); |
eff02e4f ILT |
378 | static int f22 (int) __attribute__ ((noinline)); |
379 | static int f23 (int, int) __attribute__ ((noinline)); | |
380 | ||
381 | static int | |
382 | test3 (void) | |
383 | { | |
384 | return f22 (__LINE__) + 1; | |
385 | } | |
386 | ||
387 | static int | |
388 | f22 (int f1line) | |
389 | { | |
390 | return f23 (f1line, __LINE__) + 2; | |
391 | } | |
392 | ||
393 | static int | |
394 | f23 (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; | |
068ef6d1 | 489 | default: |
eff02e4f ILT |
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 | ||
bd3e497d | 526 | static inline int test4 (void) __attribute__ ((always_inline, unused)); |
eff02e4f ILT |
527 | static inline int f32 (int) __attribute__ ((always_inline)); |
528 | static inline int f33 (int, int) __attribute__ ((always_inline)); | |
529 | ||
530 | static inline int | |
531 | test4 (void) | |
532 | { | |
533 | return f32 (__LINE__) + 1; | |
534 | } | |
535 | ||
536 | static inline int | |
537 | f32 (int f1line) | |
538 | { | |
539 | return f33 (f1line, __LINE__) + 2; | |
540 | } | |
541 | ||
542 | static inline int | |
543 | f33 (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 | ||
601 | static void | |
602 | error_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 | ||
614 | int | |
615 | main (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 | } |