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