xref: /netbsd-src/external/gpl3/gdb/dist/libbacktrace/zstdtest.c (revision f8cf1a9151c7af1cb0bd8b09c13c66bca599c027)
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