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