1/* mtest.c -- Minidebug test for libbacktrace library 2 Copyright (C) 2020-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 using libbacktrace with a program that uses the 34 minidebuginfo format in a .gnu_debugdata section. See 35 https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html 36 for a bit more information about minidebuginfo. What is relevant 37 for libbacktrace is that we have just a symbol table, with no debug 38 info, so we should be able to do a function backtrace, but we can't 39 do a file/line backtrace. */ 40 41#include <assert.h> 42#include <stdlib.h> 43#include <string.h> 44 45#include "backtrace.h" 46#include "backtrace-supported.h" 47 48#include "testlib.h" 49 50static int test1 (void) __attribute__ ((noinline, noclone, unused)); 51static int f2 (int) __attribute__ ((noinline, noclone)); 52static int f3 (int, int) __attribute__ ((noinline, noclone)); 53 54/* Collected PC values. */ 55 56static uintptr_t addrs[20]; 57 58/* The backtrace callback function. This is like callback_one in 59 testlib.c, but it saves the PC also. */ 60 61static int 62callback_mtest (void *vdata, uintptr_t pc, const char *filename, int lineno, 63 const char *function) 64{ 65 struct bdata *data = (struct bdata *) vdata; 66 67 if (data->index >= sizeof addrs / sizeof addrs[0]) 68 { 69 fprintf (stderr, "callback_mtest: callback called too many times\n"); 70 data->failed = 1; 71 return 1; 72 } 73 74 addrs[data->index] = pc; 75 76 return callback_one (vdata, pc, filename, lineno, function); 77} 78 79/* Test the backtrace function with non-inlined functions. (We don't 80 test with inlined functions because they won't work with minidebug 81 anyhow.) */ 82 83static int 84test1 (void) 85{ 86 /* Returning a value here and elsewhere avoids a tailcall which 87 would mess up the backtrace. */ 88 return f2 (__LINE__) + 1; 89} 90 91static int 92f2 (int f1line) 93{ 94 return f3 (f1line, __LINE__) + 2; 95} 96 97static int 98f3 (int f1line __attribute__ ((unused)), int f2line __attribute__ ((unused))) 99{ 100 struct info all[20]; 101 struct bdata data; 102 int i; 103 size_t j; 104 105 data.all = &all[0]; 106 data.index = 0; 107 data.max = 20; 108 data.failed = 0; 109 110 i = backtrace_full (state, 0, callback_mtest, error_callback_one, &data); 111 112 if (i != 0) 113 { 114 fprintf (stderr, "test1: unexpected return value %d\n", i); 115 data.failed = 1; 116 } 117 118 if (data.index < 3) 119 { 120 fprintf (stderr, 121 "test1: not enough frames; got %zu, expected at least 3\n", 122 data.index); 123 data.failed = 1; 124 } 125 126 /* When using minidebug we don't expect the function name here. */ 127 128 for (j = 0; j < 3 && j < data.index; j++) 129 { 130 if (all[j].function == NULL) 131 { 132 struct symdata symdata; 133 134 symdata.name = NULL; 135 symdata.val = 0; 136 symdata.size = 0; 137 symdata.failed = 0; 138 139 i = backtrace_syminfo (state, addrs[j], callback_three, 140 error_callback_three, &symdata); 141 if (i == 0) 142 { 143 fprintf (stderr, 144 ("test1: [%zu], unexpected return value from " 145 "backtrace_syminfo %d\n"), 146 j, i); 147 data.failed = 1; 148 } 149 else if (symdata.name == NULL) 150 { 151 fprintf (stderr, "test1: [%zu]: syminfo did not find name\n", j); 152 data.failed = 1; 153 } 154 else 155 all[j].function = strdup (symdata.name); 156 } 157 } 158 159 if (data.index > 0) 160 { 161 if (all[0].function == NULL) 162 { 163 fprintf (stderr, "test1: [0]: missing function name\n"); 164 data.failed = 1; 165 } 166 else if (strcmp (all[0].function, "f3") != 0) 167 { 168 fprintf (stderr, "test1: [0]: got %s expected %s\n", 169 all[0].function, "f3"); 170 data.failed = 1; 171 } 172 } 173 174 if (data.index > 1) 175 { 176 if (all[1].function == NULL) 177 { 178 fprintf (stderr, "test1: [1]: missing function name\n"); 179 data.failed = 1; 180 } 181 else if (strcmp (all[1].function, "f2") != 0) 182 { 183 fprintf (stderr, "test1: [1]: got %s expected %s\n", 184 all[0].function, "f2"); 185 data.failed = 1; 186 } 187 } 188 189 if (data.index > 2) 190 { 191 if (all[2].function == NULL) 192 { 193 fprintf (stderr, "test1: [2]: missing function name\n"); 194 data.failed = 1; 195 } 196 else if (strcmp (all[2].function, "test1") != 0) 197 { 198 fprintf (stderr, "test1: [2]: got %s expected %s\n", 199 all[0].function, "test1"); 200 data.failed = 1; 201 } 202 } 203 204 printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS"); 205 206 if (data.failed) 207 ++failures; 208 209 return failures; 210} 211 212/* Test the backtrace_simple function with non-inlined functions. */ 213 214static int test3 (void) __attribute__ ((noinline, noclone, unused)); 215static int f22 (int) __attribute__ ((noinline, noclone)); 216static int f23 (int, int) __attribute__ ((noinline, noclone)); 217 218static int 219test3 (void) 220{ 221 return f22 (__LINE__) + 1; 222} 223 224static int 225f22 (int f1line) 226{ 227 return f23 (f1line, __LINE__) + 2; 228} 229 230static int 231f23 (int f1line __attribute__ ((unused)), int f2line __attribute__ ((unused))) 232{ 233 uintptr_t addrs[20]; 234 struct sdata data; 235 int i; 236 237 data.addrs = &addrs[0]; 238 data.index = 0; 239 data.max = 20; 240 data.failed = 0; 241 242 i = backtrace_simple (state, 0, callback_two, error_callback_two, &data); 243 244 if (i != 0) 245 { 246 fprintf (stderr, "test3: unexpected return value %d\n", i); 247 data.failed = 1; 248 } 249 250 if (!data.failed) 251 { 252 int j; 253 254 for (j = 0; j < 3; ++j) 255 { 256 struct symdata symdata; 257 258 symdata.name = NULL; 259 symdata.val = 0; 260 symdata.size = 0; 261 symdata.failed = 0; 262 263 i = backtrace_syminfo (state, addrs[j], callback_three, 264 error_callback_three, &symdata); 265 if (i == 0) 266 { 267 fprintf (stderr, 268 ("test3: [%d]: unexpected return value " 269 "from backtrace_syminfo %d\n"), 270 j, i); 271 symdata.failed = 1; 272 } 273 274 if (!symdata.failed) 275 { 276 const char *expected; 277 278 switch (j) 279 { 280 case 0: 281 expected = "f23"; 282 break; 283 case 1: 284 expected = "f22"; 285 break; 286 case 2: 287 expected = "test3"; 288 break; 289 default: 290 assert (0); 291 } 292 293 if (symdata.name == NULL) 294 { 295 fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j); 296 symdata.failed = 1; 297 } 298 /* Use strncmp, not strcmp, because GCC might create a 299 clone. */ 300 else if (strncmp (symdata.name, expected, strlen (expected)) 301 != 0) 302 { 303 fprintf (stderr, 304 ("test3: [%d]: unexpected syminfo name " 305 "got %s expected %s\n"), 306 j, symdata.name, expected); 307 symdata.failed = 1; 308 } 309 } 310 311 if (symdata.failed) 312 data.failed = 1; 313 } 314 } 315 316 printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS"); 317 318 if (data.failed) 319 ++failures; 320 321 return failures; 322} 323 324int test5 (void) __attribute__ ((unused)); 325 326int global = 1; 327 328int 329test5 (void) 330{ 331 struct symdata symdata; 332 int i; 333 uintptr_t addr = (uintptr_t) &global; 334 335 if (sizeof (global) > 1) 336 addr += 1; 337 338 symdata.name = NULL; 339 symdata.val = 0; 340 symdata.size = 0; 341 symdata.failed = 0; 342 343 i = backtrace_syminfo (state, addr, callback_three, 344 error_callback_three, &symdata); 345 if (i == 0) 346 { 347 fprintf (stderr, 348 "test5: unexpected return value from backtrace_syminfo %d\n", 349 i); 350 symdata.failed = 1; 351 } 352 353 if (!symdata.failed) 354 { 355 if (symdata.name == NULL) 356 { 357 fprintf (stderr, "test5: NULL syminfo name\n"); 358 symdata.failed = 1; 359 } 360 else if (!(strncmp (symdata.name, "global", 6) == 0 361 && (symdata.name[6] == '\0'|| symdata.name[6] == '.'))) 362 { 363 fprintf (stderr, 364 "test5: unexpected syminfo name got %s expected %s\n", 365 symdata.name, "global"); 366 symdata.failed = 1; 367 } 368 else if (symdata.val != (uintptr_t) &global) 369 { 370 fprintf (stderr, 371 "test5: unexpected syminfo value got %lx expected %lx\n", 372 (unsigned long) symdata.val, 373 (unsigned long) (uintptr_t) &global); 374 symdata.failed = 1; 375 } 376 else if (symdata.size != sizeof (global)) 377 { 378 fprintf (stderr, 379 "test5: unexpected syminfo size got %lx expected %lx\n", 380 (unsigned long) symdata.size, 381 (unsigned long) sizeof (global)); 382 symdata.failed = 1; 383 } 384 } 385 386 printf ("%s: backtrace_syminfo variable\n", 387 symdata.failed ? "FAIL" : "PASS"); 388 389 if (symdata.failed) 390 ++failures; 391 392 return failures; 393} 394 395int 396main (int argc ATTRIBUTE_UNUSED, char **argv) 397{ 398 state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, 399 error_callback_create, NULL); 400 401#if BACKTRACE_SUPPORTED 402 test1 (); 403 test3 (); 404#if BACKTRACE_SUPPORTS_DATA 405 test5 (); 406#endif 407#endif 408 409 exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); 410} 411