]>
Commit | Line | Data |
---|---|---|
eff02e4f | 1 | /* btest.c -- Test for libbacktrace library |
a945c346 | 2 | Copyright (C) 2012-2024 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 | |
84ebf639 | 10 | notice, this list of conditions and the following disclaimer. |
eff02e4f ILT |
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 | |
84ebf639 CL |
15 | distribution. |
16 | ||
eff02e4f ILT |
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> | |
65d0b859 | 40 | #include <unistd.h> |
690180eb | 41 | #include <sys/stat.h> |
eff02e4f ILT |
42 | |
43 | #include "filenames.h" | |
44 | ||
45 | #include "backtrace.h" | |
46 | #include "backtrace-supported.h" | |
47 | ||
d1609a23 | 48 | #include "testlib.h" |
eff02e4f ILT |
49 | |
50 | /* Test the backtrace function with non-inlined functions. */ | |
51 | ||
c51b2c8c TV |
52 | static int test1 (void) __attribute__ ((noinline, noclone, unused)); |
53 | static int f2 (int) __attribute__ ((noinline, noclone)); | |
54 | static int f3 (int, int) __attribute__ ((noinline, noclone)); | |
eff02e4f ILT |
55 | |
56 | static int | |
57 | test1 (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 | ||
64 | static int | |
65 | f2 (int f1line) | |
66 | { | |
67 | return f3 (f1line, __LINE__) + 2; | |
68 | } | |
69 | ||
70 | static int | |
71 | f3 (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 | ||
b8ddd61b ILT |
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 | ||
d1609a23 ILT |
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); | |
eff02e4f ILT |
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 | ||
bd3e497d | 114 | static inline int test2 (void) __attribute__ ((always_inline, unused)); |
eff02e4f ILT |
115 | static inline int f12 (int) __attribute__ ((always_inline)); |
116 | static inline int f13 (int, int) __attribute__ ((always_inline)); | |
117 | ||
118 | static inline int | |
119 | test2 (void) | |
120 | { | |
121 | return f12 (__LINE__) + 1; | |
122 | } | |
123 | ||
124 | static inline int | |
125 | f12 (int f1line) | |
126 | { | |
127 | return f13 (f1line, __LINE__) + 2; | |
128 | } | |
129 | ||
130 | static inline int | |
131 | f13 (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 | ||
d1609a23 ILT |
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); | |
eff02e4f ILT |
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 | ||
c51b2c8c TV |
166 | static int test3 (void) __attribute__ ((noinline, noclone, unused)); |
167 | static int f22 (int) __attribute__ ((noinline, noclone)); | |
168 | static int f23 (int, int) __attribute__ ((noinline, noclone)); | |
eff02e4f ILT |
169 | |
170 | static int | |
171 | test3 (void) | |
172 | { | |
173 | return f22 (__LINE__) + 1; | |
174 | } | |
175 | ||
176 | static int | |
177 | f22 (int f1line) | |
178 | { | |
179 | return f23 (f1line, __LINE__) + 2; | |
180 | } | |
181 | ||
182 | static int | |
183 | f23 (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 | } | |
84ebf639 | 235 | } |
eff02e4f | 236 | |
d1609a23 ILT |
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); | |
eff02e4f ILT |
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; | |
1f96a712 | 250 | symdata.size = 0; |
eff02e4f ILT |
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; | |
068ef6d1 | 279 | default: |
eff02e4f ILT |
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 | ||
bd3e497d | 316 | static inline int test4 (void) __attribute__ ((always_inline, unused)); |
eff02e4f ILT |
317 | static inline int f32 (int) __attribute__ ((always_inline)); |
318 | static inline int f33 (int, int) __attribute__ ((always_inline)); | |
319 | ||
320 | static inline int | |
321 | test4 (void) | |
322 | { | |
323 | return f32 (__LINE__) + 1; | |
324 | } | |
325 | ||
326 | static inline int | |
327 | f32 (int f1line) | |
328 | { | |
329 | return f33 (f1line, __LINE__) + 2; | |
330 | } | |
331 | ||
332 | static inline int | |
333 | f33 (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 | ||
d1609a23 ILT |
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); | |
eff02e4f ILT |
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 | ||
121eb024 | 391 | static int test5 (void) __attribute__ ((unused)); |
e24afc10 | 392 | |
cfa658e4 ILT |
393 | int global = 1; |
394 | ||
395 | static int | |
396 | test5 (void) | |
397 | { | |
398 | struct symdata symdata; | |
399 | int i; | |
1f96a712 JJ |
400 | uintptr_t addr = (uintptr_t) &global; |
401 | ||
402 | if (sizeof (global) > 1) | |
403 | addr += 1; | |
cfa658e4 ILT |
404 | |
405 | symdata.name = NULL; | |
406 | symdata.val = 0; | |
1f96a712 | 407 | symdata.size = 0; |
cfa658e4 ILT |
408 | symdata.failed = 0; |
409 | ||
1f96a712 | 410 | i = backtrace_syminfo (state, addr, callback_three, |
cfa658e4 ILT |
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 | } | |
067aef03 TV |
427 | else if (!(strncmp (symdata.name, "global", 6) == 0 |
428 | && (symdata.name[6] == '\0'|| symdata.name[6] == '.'))) | |
cfa658e4 ILT |
429 | { |
430 | fprintf (stderr, | |
431 | "test5: unexpected syminfo name got %s expected %s\n", | |
432 | symdata.name, "global"); | |
433 | symdata.failed = 1; | |
434 | } | |
1f96a712 JJ |
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 | } | |
cfa658e4 ILT |
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 | ||
690180eb ML |
462 | #define MIN_DESCRIPTOR 3 |
463 | #define MAX_DESCRIPTOR 10 | |
464 | ||
465 | static int fstat_status[MAX_DESCRIPTOR]; | |
466 | ||
467 | /* Check files that are available. */ | |
468 | ||
469 | static void | |
470 | check_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 | ||
65d0b859 ILT |
477 | /* Check that are no files left open. */ |
478 | ||
479 | static void | |
480 | check_open_files (void) | |
481 | { | |
690180eb | 482 | for (unsigned i = MIN_DESCRIPTOR; i < MAX_DESCRIPTOR; i++) |
65d0b859 | 483 | { |
690180eb | 484 | if (fstat_status[i] != 0 && close (i) == 0) |
65d0b859 ILT |
485 | { |
486 | fprintf (stderr, | |
487 | "ERROR: descriptor %d still open after tests complete\n", | |
488 | i); | |
489 | ++failures; | |
490 | } | |
491 | } | |
492 | } | |
493 | ||
eff02e4f ILT |
494 | /* Run all the tests. */ |
495 | ||
496 | int | |
497 | main (int argc ATTRIBUTE_UNUSED, char **argv) | |
498 | { | |
690180eb ML |
499 | check_available_files (); |
500 | ||
eff02e4f ILT |
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 (); | |
e24afc10 | 509 | #if BACKTRACE_SUPPORTS_DATA |
cfa658e4 | 510 | test5 (); |
e24afc10 | 511 | #endif |
eff02e4f ILT |
512 | #endif |
513 | ||
65d0b859 ILT |
514 | check_open_files (); |
515 | ||
eff02e4f ILT |
516 | exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); |
517 | } |