1 1.1 christos /* mtest.c -- Minidebug test for libbacktrace library 2 1.1.1.2 christos Copyright (C) 2020-2024 Free Software Foundation, Inc. 3 1.1 christos Written by Ian Lance Taylor, Google. 4 1.1 christos 5 1.1 christos Redistribution and use in source and binary forms, with or without 6 1.1 christos modification, are permitted provided that the following conditions are 7 1.1 christos met: 8 1.1 christos 9 1.1 christos (1) Redistributions of source code must retain the above copyright 10 1.1 christos notice, this list of conditions and the following disclaimer. 11 1.1 christos 12 1.1 christos (2) Redistributions in binary form must reproduce the above copyright 13 1.1 christos notice, this list of conditions and the following disclaimer in 14 1.1 christos the documentation and/or other materials provided with the 15 1.1 christos distribution. 16 1.1 christos 17 1.1 christos (3) The name of the author may not be used to 18 1.1 christos endorse or promote products derived from this software without 19 1.1 christos specific prior written permission. 20 1.1 christos 21 1.1 christos THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 22 1.1 christos IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 23 1.1 christos WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 24 1.1 christos DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, 25 1.1 christos INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 26 1.1 christos (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 27 1.1 christos SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28 1.1 christos HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 29 1.1 christos STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 30 1.1 christos IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 1.1 christos POSSIBILITY OF SUCH DAMAGE. */ 32 1.1 christos 33 1.1 christos /* This program tests using libbacktrace with a program that uses the 34 1.1 christos minidebuginfo format in a .gnu_debugdata section. See 35 1.1 christos https://sourceware.org/gdb/current/onlinedocs/gdb/MiniDebugInfo.html 36 1.1 christos for a bit more information about minidebuginfo. What is relevant 37 1.1 christos for libbacktrace is that we have just a symbol table, with no debug 38 1.1 christos info, so we should be able to do a function backtrace, but we can't 39 1.1 christos do a file/line backtrace. */ 40 1.1 christos 41 1.1 christos #include <assert.h> 42 1.1 christos #include <stdlib.h> 43 1.1 christos #include <string.h> 44 1.1 christos 45 1.1 christos #include "backtrace.h" 46 1.1 christos #include "backtrace-supported.h" 47 1.1 christos 48 1.1 christos #include "testlib.h" 49 1.1 christos 50 1.1 christos static int test1 (void) __attribute__ ((noinline, noclone, unused)); 51 1.1 christos static int f2 (int) __attribute__ ((noinline, noclone)); 52 1.1 christos static int f3 (int, int) __attribute__ ((noinline, noclone)); 53 1.1 christos 54 1.1 christos /* Collected PC values. */ 55 1.1 christos 56 1.1 christos static uintptr_t addrs[20]; 57 1.1 christos 58 1.1 christos /* The backtrace callback function. This is like callback_one in 59 1.1 christos testlib.c, but it saves the PC also. */ 60 1.1 christos 61 1.1 christos static int 62 1.1 christos callback_mtest (void *vdata, uintptr_t pc, const char *filename, int lineno, 63 1.1 christos const char *function) 64 1.1 christos { 65 1.1 christos struct bdata *data = (struct bdata *) vdata; 66 1.1 christos 67 1.1 christos if (data->index >= sizeof addrs / sizeof addrs[0]) 68 1.1 christos { 69 1.1 christos fprintf (stderr, "callback_mtest: callback called too many times\n"); 70 1.1 christos data->failed = 1; 71 1.1 christos return 1; 72 1.1 christos } 73 1.1 christos 74 1.1 christos addrs[data->index] = pc; 75 1.1 christos 76 1.1 christos return callback_one (vdata, pc, filename, lineno, function); 77 1.1 christos } 78 1.1 christos 79 1.1 christos /* Test the backtrace function with non-inlined functions. (We don't 80 1.1 christos test with inlined functions because they won't work with minidebug 81 1.1 christos anyhow.) */ 82 1.1 christos 83 1.1 christos static int 84 1.1 christos test1 (void) 85 1.1 christos { 86 1.1 christos /* Returning a value here and elsewhere avoids a tailcall which 87 1.1 christos would mess up the backtrace. */ 88 1.1 christos return f2 (__LINE__) + 1; 89 1.1 christos } 90 1.1 christos 91 1.1 christos static int 92 1.1 christos f2 (int f1line) 93 1.1 christos { 94 1.1 christos return f3 (f1line, __LINE__) + 2; 95 1.1 christos } 96 1.1 christos 97 1.1 christos static int 98 1.1 christos f3 (int f1line __attribute__ ((unused)), int f2line __attribute__ ((unused))) 99 1.1 christos { 100 1.1 christos struct info all[20]; 101 1.1 christos struct bdata data; 102 1.1 christos int i; 103 1.1 christos size_t j; 104 1.1 christos 105 1.1 christos data.all = &all[0]; 106 1.1 christos data.index = 0; 107 1.1 christos data.max = 20; 108 1.1 christos data.failed = 0; 109 1.1 christos 110 1.1 christos i = backtrace_full (state, 0, callback_mtest, error_callback_one, &data); 111 1.1 christos 112 1.1 christos if (i != 0) 113 1.1 christos { 114 1.1 christos fprintf (stderr, "test1: unexpected return value %d\n", i); 115 1.1 christos data.failed = 1; 116 1.1 christos } 117 1.1 christos 118 1.1 christos if (data.index < 3) 119 1.1 christos { 120 1.1 christos fprintf (stderr, 121 1.1 christos "test1: not enough frames; got %zu, expected at least 3\n", 122 1.1 christos data.index); 123 1.1 christos data.failed = 1; 124 1.1 christos } 125 1.1 christos 126 1.1 christos /* When using minidebug we don't expect the function name here. */ 127 1.1 christos 128 1.1 christos for (j = 0; j < 3 && j < data.index; j++) 129 1.1 christos { 130 1.1 christos if (all[j].function == NULL) 131 1.1 christos { 132 1.1 christos struct symdata symdata; 133 1.1 christos 134 1.1 christos symdata.name = NULL; 135 1.1 christos symdata.val = 0; 136 1.1 christos symdata.size = 0; 137 1.1 christos symdata.failed = 0; 138 1.1 christos 139 1.1 christos i = backtrace_syminfo (state, addrs[j], callback_three, 140 1.1 christos error_callback_three, &symdata); 141 1.1 christos if (i == 0) 142 1.1 christos { 143 1.1 christos fprintf (stderr, 144 1.1 christos ("test1: [%zu], unexpected return value from " 145 1.1 christos "backtrace_syminfo %d\n"), 146 1.1 christos j, i); 147 1.1 christos data.failed = 1; 148 1.1 christos } 149 1.1 christos else if (symdata.name == NULL) 150 1.1 christos { 151 1.1 christos fprintf (stderr, "test1: [%zu]: syminfo did not find name\n", j); 152 1.1 christos data.failed = 1; 153 1.1 christos } 154 1.1 christos else 155 1.1 christos all[j].function = strdup (symdata.name); 156 1.1 christos } 157 1.1 christos } 158 1.1 christos 159 1.1 christos if (data.index > 0) 160 1.1 christos { 161 1.1 christos if (all[0].function == NULL) 162 1.1 christos { 163 1.1 christos fprintf (stderr, "test1: [0]: missing function name\n"); 164 1.1 christos data.failed = 1; 165 1.1 christos } 166 1.1 christos else if (strcmp (all[0].function, "f3") != 0) 167 1.1 christos { 168 1.1 christos fprintf (stderr, "test1: [0]: got %s expected %s\n", 169 1.1 christos all[0].function, "f3"); 170 1.1 christos data.failed = 1; 171 1.1 christos } 172 1.1 christos } 173 1.1 christos 174 1.1 christos if (data.index > 1) 175 1.1 christos { 176 1.1 christos if (all[1].function == NULL) 177 1.1 christos { 178 1.1 christos fprintf (stderr, "test1: [1]: missing function name\n"); 179 1.1 christos data.failed = 1; 180 1.1 christos } 181 1.1 christos else if (strcmp (all[1].function, "f2") != 0) 182 1.1 christos { 183 1.1 christos fprintf (stderr, "test1: [1]: got %s expected %s\n", 184 1.1 christos all[0].function, "f2"); 185 1.1 christos data.failed = 1; 186 1.1 christos } 187 1.1 christos } 188 1.1 christos 189 1.1 christos if (data.index > 2) 190 1.1 christos { 191 1.1 christos if (all[2].function == NULL) 192 1.1 christos { 193 1.1 christos fprintf (stderr, "test1: [2]: missing function name\n"); 194 1.1 christos data.failed = 1; 195 1.1 christos } 196 1.1 christos else if (strcmp (all[2].function, "test1") != 0) 197 1.1 christos { 198 1.1 christos fprintf (stderr, "test1: [2]: got %s expected %s\n", 199 1.1 christos all[0].function, "test1"); 200 1.1 christos data.failed = 1; 201 1.1 christos } 202 1.1 christos } 203 1.1 christos 204 1.1 christos printf ("%s: backtrace_full noinline\n", data.failed ? "FAIL" : "PASS"); 205 1.1 christos 206 1.1 christos if (data.failed) 207 1.1 christos ++failures; 208 1.1 christos 209 1.1 christos return failures; 210 1.1 christos } 211 1.1 christos 212 1.1 christos /* Test the backtrace_simple function with non-inlined functions. */ 213 1.1 christos 214 1.1 christos static int test3 (void) __attribute__ ((noinline, noclone, unused)); 215 1.1 christos static int f22 (int) __attribute__ ((noinline, noclone)); 216 1.1 christos static int f23 (int, int) __attribute__ ((noinline, noclone)); 217 1.1 christos 218 1.1 christos static int 219 1.1 christos test3 (void) 220 1.1 christos { 221 1.1 christos return f22 (__LINE__) + 1; 222 1.1 christos } 223 1.1 christos 224 1.1 christos static int 225 1.1 christos f22 (int f1line) 226 1.1 christos { 227 1.1 christos return f23 (f1line, __LINE__) + 2; 228 1.1 christos } 229 1.1 christos 230 1.1 christos static int 231 1.1 christos f23 (int f1line __attribute__ ((unused)), int f2line __attribute__ ((unused))) 232 1.1 christos { 233 1.1 christos uintptr_t addrs[20]; 234 1.1 christos struct sdata data; 235 1.1 christos int i; 236 1.1 christos 237 1.1 christos data.addrs = &addrs[0]; 238 1.1 christos data.index = 0; 239 1.1 christos data.max = 20; 240 1.1 christos data.failed = 0; 241 1.1 christos 242 1.1 christos i = backtrace_simple (state, 0, callback_two, error_callback_two, &data); 243 1.1 christos 244 1.1 christos if (i != 0) 245 1.1 christos { 246 1.1 christos fprintf (stderr, "test3: unexpected return value %d\n", i); 247 1.1 christos data.failed = 1; 248 1.1 christos } 249 1.1 christos 250 1.1 christos if (!data.failed) 251 1.1 christos { 252 1.1 christos int j; 253 1.1 christos 254 1.1 christos for (j = 0; j < 3; ++j) 255 1.1 christos { 256 1.1 christos struct symdata symdata; 257 1.1 christos 258 1.1 christos symdata.name = NULL; 259 1.1 christos symdata.val = 0; 260 1.1 christos symdata.size = 0; 261 1.1 christos symdata.failed = 0; 262 1.1 christos 263 1.1 christos i = backtrace_syminfo (state, addrs[j], callback_three, 264 1.1 christos error_callback_three, &symdata); 265 1.1 christos if (i == 0) 266 1.1 christos { 267 1.1 christos fprintf (stderr, 268 1.1 christos ("test3: [%d]: unexpected return value " 269 1.1 christos "from backtrace_syminfo %d\n"), 270 1.1 christos j, i); 271 1.1 christos symdata.failed = 1; 272 1.1 christos } 273 1.1 christos 274 1.1 christos if (!symdata.failed) 275 1.1 christos { 276 1.1 christos const char *expected; 277 1.1 christos 278 1.1 christos switch (j) 279 1.1 christos { 280 1.1 christos case 0: 281 1.1 christos expected = "f23"; 282 1.1 christos break; 283 1.1 christos case 1: 284 1.1 christos expected = "f22"; 285 1.1 christos break; 286 1.1 christos case 2: 287 1.1 christos expected = "test3"; 288 1.1 christos break; 289 1.1 christos default: 290 1.1 christos assert (0); 291 1.1 christos } 292 1.1 christos 293 1.1 christos if (symdata.name == NULL) 294 1.1 christos { 295 1.1 christos fprintf (stderr, "test3: [%d]: NULL syminfo name\n", j); 296 1.1 christos symdata.failed = 1; 297 1.1 christos } 298 1.1 christos /* Use strncmp, not strcmp, because GCC might create a 299 1.1 christos clone. */ 300 1.1 christos else if (strncmp (symdata.name, expected, strlen (expected)) 301 1.1 christos != 0) 302 1.1 christos { 303 1.1 christos fprintf (stderr, 304 1.1 christos ("test3: [%d]: unexpected syminfo name " 305 1.1 christos "got %s expected %s\n"), 306 1.1 christos j, symdata.name, expected); 307 1.1 christos symdata.failed = 1; 308 1.1 christos } 309 1.1 christos } 310 1.1 christos 311 1.1 christos if (symdata.failed) 312 1.1 christos data.failed = 1; 313 1.1 christos } 314 1.1 christos } 315 1.1 christos 316 1.1 christos printf ("%s: backtrace_simple noinline\n", data.failed ? "FAIL" : "PASS"); 317 1.1 christos 318 1.1 christos if (data.failed) 319 1.1 christos ++failures; 320 1.1 christos 321 1.1 christos return failures; 322 1.1 christos } 323 1.1 christos 324 1.1 christos int test5 (void) __attribute__ ((unused)); 325 1.1 christos 326 1.1 christos int global = 1; 327 1.1 christos 328 1.1 christos int 329 1.1 christos test5 (void) 330 1.1 christos { 331 1.1 christos struct symdata symdata; 332 1.1 christos int i; 333 1.1 christos uintptr_t addr = (uintptr_t) &global; 334 1.1 christos 335 1.1 christos if (sizeof (global) > 1) 336 1.1 christos addr += 1; 337 1.1 christos 338 1.1 christos symdata.name = NULL; 339 1.1 christos symdata.val = 0; 340 1.1 christos symdata.size = 0; 341 1.1 christos symdata.failed = 0; 342 1.1 christos 343 1.1 christos i = backtrace_syminfo (state, addr, callback_three, 344 1.1 christos error_callback_three, &symdata); 345 1.1 christos if (i == 0) 346 1.1 christos { 347 1.1 christos fprintf (stderr, 348 1.1 christos "test5: unexpected return value from backtrace_syminfo %d\n", 349 1.1 christos i); 350 1.1 christos symdata.failed = 1; 351 1.1 christos } 352 1.1 christos 353 1.1 christos if (!symdata.failed) 354 1.1 christos { 355 1.1 christos if (symdata.name == NULL) 356 1.1 christos { 357 1.1 christos fprintf (stderr, "test5: NULL syminfo name\n"); 358 1.1 christos symdata.failed = 1; 359 1.1 christos } 360 1.1 christos else if (!(strncmp (symdata.name, "global", 6) == 0 361 1.1 christos && (symdata.name[6] == '\0'|| symdata.name[6] == '.'))) 362 1.1 christos { 363 1.1 christos fprintf (stderr, 364 1.1 christos "test5: unexpected syminfo name got %s expected %s\n", 365 1.1 christos symdata.name, "global"); 366 1.1 christos symdata.failed = 1; 367 1.1 christos } 368 1.1 christos else if (symdata.val != (uintptr_t) &global) 369 1.1 christos { 370 1.1 christos fprintf (stderr, 371 1.1 christos "test5: unexpected syminfo value got %lx expected %lx\n", 372 1.1 christos (unsigned long) symdata.val, 373 1.1 christos (unsigned long) (uintptr_t) &global); 374 1.1 christos symdata.failed = 1; 375 1.1 christos } 376 1.1 christos else if (symdata.size != sizeof (global)) 377 1.1 christos { 378 1.1 christos fprintf (stderr, 379 1.1 christos "test5: unexpected syminfo size got %lx expected %lx\n", 380 1.1 christos (unsigned long) symdata.size, 381 1.1 christos (unsigned long) sizeof (global)); 382 1.1 christos symdata.failed = 1; 383 1.1 christos } 384 1.1 christos } 385 1.1 christos 386 1.1 christos printf ("%s: backtrace_syminfo variable\n", 387 1.1 christos symdata.failed ? "FAIL" : "PASS"); 388 1.1 christos 389 1.1 christos if (symdata.failed) 390 1.1 christos ++failures; 391 1.1 christos 392 1.1 christos return failures; 393 1.1 christos } 394 1.1 christos 395 1.1 christos int 396 1.1 christos main (int argc ATTRIBUTE_UNUSED, char **argv) 397 1.1 christos { 398 1.1 christos state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, 399 1.1 christos error_callback_create, NULL); 400 1.1 christos 401 1.1 christos #if BACKTRACE_SUPPORTED 402 1.1 christos test1 (); 403 1.1 christos test3 (); 404 1.1 christos #if BACKTRACE_SUPPORTS_DATA 405 1.1 christos test5 (); 406 1.1 christos #endif 407 1.1 christos #endif 408 1.1 christos 409 1.1 christos exit (failures ? EXIT_FAILURE : EXIT_SUCCESS); 410 1.1 christos } 411