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