1 /* ztest.c -- Test for libbacktrace zstd code. 2 Copyright (C) 2022-2024 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 #include "config.h" 34 35 #include <errno.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <string.h> 39 #include <time.h> 40 #include <sys/types.h> 41 #include <sys/stat.h> 42 43 #ifdef HAVE_ZSTD 44 #include <zstd.h> 45 #endif 46 47 #include "backtrace.h" 48 #include "backtrace-supported.h" 49 50 #include "internal.h" 51 #include "testlib.h" 52 53 #ifndef HAVE_CLOCK_GETTIME 54 55 typedef int xclockid_t; 56 57 static int 58 xclock_gettime (xclockid_t id ATTRIBUTE_UNUSED, 59 struct timespec *ts ATTRIBUTE_UNUSED) 60 { 61 errno = EINVAL; 62 return -1; 63 } 64 65 #define clockid_t xclockid_t 66 #define clock_gettime xclock_gettime 67 #undef CLOCK_REALTIME 68 #define CLOCK_REALTIME 0 69 70 #endif /* !defined(HAVE_CLOCK_GETTIME) */ 71 72 #ifdef CLOCK_PROCESS_CPUTIME_ID 73 #define ZSTD_CLOCK_GETTIME_ARG CLOCK_PROCESS_CPUTIME_ID 74 #else 75 #define ZSTD_CLOCK_GETTIME_ARG CLOCK_REALTIME 76 #endif 77 78 /* Some tests for the local zstd inflation code. */ 79 80 struct zstd_test 81 { 82 const char *name; 83 const char *uncompressed; 84 size_t uncompressed_len; 85 const char *compressed; 86 size_t compressed_len; 87 }; 88 89 /* Error callback. */ 90 91 static void 92 error_callback_compress (void *vdata ATTRIBUTE_UNUSED, const char *msg, 93 int errnum) 94 { 95 fprintf (stderr, "%s", msg); 96 if (errnum > 0) 97 fprintf (stderr, ": %s", strerror (errnum)); 98 fprintf (stderr, "\n"); 99 exit (EXIT_FAILURE); 100 } 101 102 static const struct zstd_test tests[] = 103 { 104 { 105 "empty", 106 "", 107 0, 108 "\x28\xb5\x2f\xfd\x24\x00\x01\x00\x00\x99\xe9\xd8\x51", 109 13, 110 }, 111 { 112 "hello", 113 "hello, world\n", 114 0, 115 ("\x28\xb5\x2f\xfd\x24\x0d\x69\x00\x00\x68\x65\x6c\x6c\x6f\x2c\x20" 116 "\x77\x6f\x72\x6c\x64\x0a\x4c\x1f\xf9\xf1"), 117 26, 118 }, 119 { 120 "goodbye", 121 "goodbye, world", 122 0, 123 ("\x28\xb5\x2f\xfd\x24\x0e\x71\x00\x00\x67\x6f\x6f\x64\x62\x79\x65" 124 "\x2c\x20\x77\x6f\x72\x6c\x64\x61\x7b\x4b\x83"), 125 27, 126 }, 127 { 128 "ranges", 129 ("\xcc\x11\x00\x00\x00\x00\x00\x00\xd5\x13\x00\x00\x00\x00\x00\x00" 130 "\x1c\x14\x00\x00\x00\x00\x00\x00\x72\x14\x00\x00\x00\x00\x00\x00" 131 "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" 132 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 133 "\xfb\x12\x00\x00\x00\x00\x00\x00\x09\x13\x00\x00\x00\x00\x00\x00" 134 "\x0c\x13\x00\x00\x00\x00\x00\x00\xcb\x13\x00\x00\x00\x00\x00\x00" 135 "\x29\x14\x00\x00\x00\x00\x00\x00\x4e\x14\x00\x00\x00\x00\x00\x00" 136 "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" 137 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 138 "\xfb\x12\x00\x00\x00\x00\x00\x00\x09\x13\x00\x00\x00\x00\x00\x00" 139 "\x67\x13\x00\x00\x00\x00\x00\x00\xcb\x13\x00\x00\x00\x00\x00\x00" 140 "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00" 141 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 142 "\x5f\x0b\x00\x00\x00\x00\x00\x00\x6c\x0b\x00\x00\x00\x00\x00\x00" 143 "\x7d\x0b\x00\x00\x00\x00\x00\x00\x7e\x0c\x00\x00\x00\x00\x00\x00" 144 "\x38\x0f\x00\x00\x00\x00\x00\x00\x5c\x0f\x00\x00\x00\x00\x00\x00" 145 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 146 "\x83\x0c\x00\x00\x00\x00\x00\x00\xfa\x0c\x00\x00\x00\x00\x00\x00" 147 "\xfd\x0d\x00\x00\x00\x00\x00\x00\xef\x0e\x00\x00\x00\x00\x00\x00" 148 "\x14\x0f\x00\x00\x00\x00\x00\x00\x38\x0f\x00\x00\x00\x00\x00\x00" 149 "\x9f\x0f\x00\x00\x00\x00\x00\x00\xac\x0f\x00\x00\x00\x00\x00\x00" 150 "\xdb\x0f\x00\x00\x00\x00\x00\x00\xff\x0f\x00\x00\x00\x00\x00\x00" 151 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 152 "\xfd\x0d\x00\x00\x00\x00\x00\x00\xd8\x0e\x00\x00\x00\x00\x00\x00" 153 "\x9f\x0f\x00\x00\x00\x00\x00\x00\xac\x0f\x00\x00\x00\x00\x00\x00" 154 "\xdb\x0f\x00\x00\x00\x00\x00\x00\xff\x0f\x00\x00\x00\x00\x00\x00" 155 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 156 "\xfa\x0c\x00\x00\x00\x00\x00\x00\xea\x0d\x00\x00\x00\x00\x00\x00" 157 "\xef\x0e\x00\x00\x00\x00\x00\x00\x14\x0f\x00\x00\x00\x00\x00\x00" 158 "\x5c\x0f\x00\x00\x00\x00\x00\x00\x9f\x0f\x00\x00\x00\x00\x00\x00" 159 "\xac\x0f\x00\x00\x00\x00\x00\x00\xdb\x0f\x00\x00\x00\x00\x00\x00" 160 "\xff\x0f\x00\x00\x00\x00\x00\x00\x2c\x10\x00\x00\x00\x00\x00\x00" 161 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 162 "\x60\x11\x00\x00\x00\x00\x00\x00\xd1\x16\x00\x00\x00\x00\x00\x00" 163 "\x40\x0b\x00\x00\x00\x00\x00\x00\x2c\x10\x00\x00\x00\x00\x00\x00" 164 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 165 "\x7a\x00\x00\x00\x00\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x00\x00" 166 "\x9f\x01\x00\x00\x00\x00\x00\x00\xa7\x01\x00\x00\x00\x00\x00\x00" 167 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" 168 "\x7a\x00\x00\x00\x00\x00\x00\x00\xa9\x00\x00\x00\x00\x00\x00\x00" 169 "\x9f\x01\x00\x00\x00\x00\x00\x00\xa7\x01\x00\x00\x00\x00\x00\x00" 170 "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"), 171 672, 172 ("\x28\xb5\x2f\xfd\x64\xa0\x01\x2d\x05\x00\xc4\x04\xcc\x11\x00\xd5" 173 "\x13\x00\x1c\x14\x00\x72\x9d\xd5\xfb\x12\x00\x09\x0c\x13\xcb\x13" 174 "\x29\x4e\x67\x5f\x0b\x6c\x0b\x7d\x0b\x7e\x0c\x38\x0f\x5c\x0f\x83" 175 "\x0c\xfa\x0c\xfd\x0d\xef\x0e\x14\x38\x9f\x0f\xac\x0f\xdb\x0f\xff" 176 "\x0f\xd8\x9f\xac\xdb\xff\xea\x5c\x2c\x10\x60\xd1\x16\x40\x0b\x7a" 177 "\x00\xb6\x00\x9f\x01\xa7\x01\xa9\x36\x20\xa0\x83\x14\x34\x63\x4a" 178 "\x21\x70\x8c\x07\x46\x03\x4e\x10\x62\x3c\x06\x4e\xc8\x8c\xb0\x32" 179 "\x2a\x59\xad\xb2\xf1\x02\x82\x7c\x33\xcb\x92\x6f\x32\x4f\x9b\xb0" 180 "\xa2\x30\xf0\xc0\x06\x1e\x98\x99\x2c\x06\x1e\xd8\xc0\x03\x56\xd8" 181 "\xc0\x03\x0f\x6c\xe0\x01\xf1\xf0\xee\x9a\xc6\xc8\x97\x99\xd1\x6c" 182 "\xb4\x21\x45\x3b\x10\xe4\x7b\x99\x4d\x8a\x36\x64\x5c\x77\x08\x02" 183 "\xcb\xe0\xce"), 184 179, 185 } 186 }; 187 188 /* Test the hand coded samples. */ 189 190 static void 191 test_samples (struct backtrace_state *state) 192 { 193 size_t i; 194 195 for (i = 0; i < sizeof tests / sizeof tests[0]; ++i) 196 { 197 unsigned char *uncompressed; 198 size_t uncompressed_len; 199 200 uncompressed_len = tests[i].uncompressed_len; 201 if (uncompressed_len == 0) 202 uncompressed_len = strlen (tests[i].uncompressed); 203 204 uncompressed = (unsigned char *) malloc (uncompressed_len); 205 if (uncompressed == NULL) 206 { 207 perror ("malloc"); 208 fprintf (stderr, "test %s: uncompress failed\n", tests[i].name); 209 ++failures; 210 continue; 211 } 212 213 if (!backtrace_uncompress_zstd (state, 214 ((const unsigned char *) 215 tests[i].compressed), 216 tests[i].compressed_len, 217 error_callback_compress, NULL, 218 uncompressed, uncompressed_len)) 219 { 220 fprintf (stderr, "test %s: uncompress failed\n", tests[i].name); 221 ++failures; 222 } 223 else 224 { 225 if (memcmp (tests[i].uncompressed, uncompressed, uncompressed_len) 226 != 0) 227 { 228 size_t j; 229 230 fprintf (stderr, "test %s: uncompressed data mismatch\n", 231 tests[i].name); 232 for (j = 0; j < uncompressed_len; ++j) 233 if (tests[i].uncompressed[j] != uncompressed[j]) 234 fprintf (stderr, " %zu: got %#x want %#x\n", j, 235 uncompressed[j], tests[i].uncompressed[j]); 236 ++failures; 237 } 238 else 239 printf ("PASS: uncompress %s\n", tests[i].name); 240 } 241 242 free (uncompressed); 243 } 244 } 245 246 #ifdef HAVE_ZSTD 247 248 /* Given a set of TRIALS timings, discard the lowest and highest 249 values and return the mean average of the rest. */ 250 251 static size_t 252 average_time (const size_t *times, size_t trials) 253 { 254 size_t imax; 255 size_t max; 256 size_t imin; 257 size_t min; 258 size_t i; 259 size_t sum; 260 261 imin = 0; 262 imax = 0; 263 min = times[0]; 264 max = times[0]; 265 for (i = 1; i < trials; ++i) 266 { 267 if (times[i] < min) 268 { 269 imin = i; 270 min = times[i]; 271 } 272 if (times[i] > max) 273 { 274 imax = i; 275 max = times[i]; 276 } 277 } 278 279 sum = 0; 280 for (i = 0; i < trials; ++i) 281 { 282 if (i != imax && i != imin) 283 sum += times[i]; 284 } 285 return sum / (trials - 2); 286 } 287 288 #endif 289 290 /* Test a larger text, if available. */ 291 292 static void 293 test_large (struct backtrace_state *state ATTRIBUTE_UNUSED) 294 { 295 #ifdef HAVE_ZSTD 296 unsigned char *orig_buf; 297 size_t orig_bufsize; 298 size_t i; 299 char *compressed_buf; 300 size_t compressed_bufsize; 301 size_t compressed_size; 302 unsigned char *uncompressed_buf; 303 size_t r; 304 clockid_t cid; 305 struct timespec ts1; 306 struct timespec ts2; 307 size_t ctime; 308 size_t ztime; 309 const size_t trials = 16; 310 size_t ctimes[16]; 311 size_t ztimes[16]; 312 static const char * const names[] = { 313 "Isaac.Newton-Opticks.txt", 314 "../libgo/go/testdata/Isaac.Newton-Opticks.txt", 315 }; 316 317 orig_buf = NULL; 318 orig_bufsize = 0; 319 uncompressed_buf = NULL; 320 compressed_buf = NULL; 321 322 for (i = 0; i < sizeof names / sizeof names[0]; ++i) 323 { 324 size_t len; 325 char *namebuf; 326 FILE *e; 327 struct stat st; 328 char *rbuf; 329 size_t got; 330 331 len = strlen (SRCDIR) + strlen (names[i]) + 2; 332 namebuf = malloc (len); 333 if (namebuf == NULL) 334 { 335 perror ("malloc"); 336 goto fail; 337 } 338 snprintf (namebuf, len, "%s/%s", SRCDIR, names[i]); 339 e = fopen (namebuf, "r"); 340 free (namebuf); 341 if (e == NULL) 342 continue; 343 if (fstat (fileno (e), &st) < 0) 344 { 345 perror ("fstat"); 346 fclose (e); 347 continue; 348 } 349 rbuf = malloc (st.st_size); 350 if (rbuf == NULL) 351 { 352 perror ("malloc"); 353 goto fail; 354 } 355 got = fread (rbuf, 1, st.st_size, e); 356 fclose (e); 357 if (got > 0) 358 { 359 orig_buf = (unsigned char *) rbuf; 360 orig_bufsize = got; 361 break; 362 } 363 free (rbuf); 364 } 365 366 if (orig_buf == NULL) 367 { 368 /* We couldn't find an input file. */ 369 printf ("UNSUPPORTED: zstd large\n"); 370 return; 371 } 372 373 compressed_bufsize = ZSTD_compressBound (orig_bufsize); 374 compressed_buf = malloc (compressed_bufsize); 375 if (compressed_buf == NULL) 376 { 377 perror ("malloc"); 378 goto fail; 379 } 380 381 r = ZSTD_compress (compressed_buf, compressed_bufsize, 382 orig_buf, orig_bufsize, 383 ZSTD_CLEVEL_DEFAULT); 384 if (ZSTD_isError (r)) 385 { 386 fprintf (stderr, "zstd compress failed: %s\n", ZSTD_getErrorName (r)); 387 goto fail; 388 } 389 compressed_size = r; 390 391 uncompressed_buf = malloc (orig_bufsize); 392 if (uncompressed_buf == NULL) 393 { 394 perror ("malloc"); 395 goto fail; 396 } 397 398 if (!backtrace_uncompress_zstd (state, (unsigned char *) compressed_buf, 399 compressed_size, 400 error_callback_compress, NULL, 401 uncompressed_buf, orig_bufsize)) 402 { 403 fprintf (stderr, "zstd large: backtrace_uncompress_zstd failed\n"); 404 goto fail; 405 } 406 407 if (memcmp (uncompressed_buf, orig_buf, orig_bufsize) != 0) 408 { 409 size_t j; 410 411 fprintf (stderr, "zstd large: uncompressed data mismatch\n"); 412 for (j = 0; j < orig_bufsize; ++j) 413 if (orig_buf[j] != uncompressed_buf[j]) 414 fprintf (stderr, " %zu: got %#x want %#x\n", j, 415 uncompressed_buf[j], orig_buf[j]); 416 goto fail; 417 } 418 419 printf ("PASS: zstd large\n"); 420 421 for (i = 0; i < trials; ++i) 422 { 423 cid = ZSTD_CLOCK_GETTIME_ARG; 424 if (clock_gettime (cid, &ts1) < 0) 425 { 426 if (errno == EINVAL) 427 return; 428 perror ("clock_gettime"); 429 return; 430 } 431 432 if (!backtrace_uncompress_zstd (state, 433 (unsigned char *) compressed_buf, 434 compressed_size, 435 error_callback_compress, NULL, 436 uncompressed_buf, 437 orig_bufsize)) 438 { 439 fprintf (stderr, 440 ("zstd large: " 441 "benchmark backtrace_uncompress_zstd failed\n")); 442 return; 443 } 444 445 if (clock_gettime (cid, &ts2) < 0) 446 { 447 perror ("clock_gettime"); 448 return; 449 } 450 451 ctime = (ts2.tv_sec - ts1.tv_sec) * 1000000000; 452 ctime += ts2.tv_nsec - ts1.tv_nsec; 453 ctimes[i] = ctime; 454 455 if (clock_gettime (cid, &ts1) < 0) 456 { 457 perror("clock_gettime"); 458 return; 459 } 460 461 r = ZSTD_decompress (uncompressed_buf, orig_bufsize, 462 compressed_buf, compressed_size); 463 464 if (clock_gettime (cid, &ts2) < 0) 465 { 466 perror ("clock_gettime"); 467 return; 468 } 469 470 if (ZSTD_isError (r)) 471 { 472 fprintf (stderr, 473 "zstd large: benchmark zlib uncompress failed: %s\n", 474 ZSTD_getErrorName (r)); 475 return; 476 } 477 478 ztime = (ts2.tv_sec - ts1.tv_sec) * 1000000000; 479 ztime += ts2.tv_nsec - ts1.tv_nsec; 480 ztimes[i] = ztime; 481 } 482 483 /* Toss the highest and lowest times and average the rest. */ 484 ctime = average_time (ctimes, trials); 485 ztime = average_time (ztimes, trials); 486 487 printf ("backtrace: %zu ns\n", ctime); 488 printf ("zstd : %zu ns\n", ztime); 489 printf ("ratio : %g\n", (double) ztime / (double) ctime); 490 491 return; 492 493 fail: 494 printf ("FAIL: zstd large\n"); 495 ++failures; 496 497 if (orig_buf != NULL) 498 free (orig_buf); 499 if (compressed_buf != NULL) 500 free (compressed_buf); 501 if (uncompressed_buf != NULL) 502 free (uncompressed_buf); 503 504 #else /* !HAVE_ZSTD */ 505 506 printf ("UNSUPPORTED: zstd large\n"); 507 508 #endif /* !HAVE_ZSTD */ 509 } 510 511 int 512 main (int argc ATTRIBUTE_UNUSED, char **argv) 513 { 514 struct backtrace_state *state; 515 516 state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS, 517 error_callback_create, NULL); 518 519 test_samples (state); 520 test_large (state); 521 522 exit (failures != 0 ? EXIT_FAILURE : EXIT_SUCCESS); 523 } 524