1 /* ztest.c -- Test for libbacktrace inflate code.
2    Copyright (C) 2017-2021 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_ZLIB
44 #include <zlib.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
xclock_gettime(xclockid_t id ATTRIBUTE_UNUSED,struct timespec * ts ATTRIBUTE_UNUSED)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 ZLIB_CLOCK_GETTIME_ARG CLOCK_PROCESS_CPUTIME_ID
74 #else
75 #define ZLIB_CLOCK_GETTIME_ARG CLOCK_REALTIME
76 #endif
77 
78 /* Some tests for the local zlib inflation code.  */
79 
80 struct zlib_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
error_callback_compress(void * vdata ATTRIBUTE_UNUSED,const char * msg,int errnum)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 zlib_test tests[] =
103 {
104   {
105     "empty",
106     "",
107     0,
108     "\x78\x9c\x03\x00\x00\x00\x00\x01",
109     8,
110   },
111   {
112     "hello",
113     "hello, world\n",
114     0,
115     ("\x78\x9c\xca\x48\xcd\xc9\xc9\xd7\x51\x28\xcf"
116      "\x2f\xca\x49\xe1\x02\x04\x00\x00\xff\xff\x21\xe7\x04\x93"),
117     25,
118   },
119   {
120     "goodbye",
121     "goodbye, world",
122     0,
123     ("\x78\x9c\x4b\xcf\xcf\x4f\x49\xaa"
124      "\x4c\xd5\x51\x28\xcf\x2f\xca\x49"
125      "\x01\x00\x28\xa5\x05\x5e"),
126     22,
127   },
128   {
129     "ranges",
130     ("\xcc\x11\x00\x00\x00\x00\x00\x00\xd5\x13\x00\x00\x00\x00\x00\x00"
131      "\x1c\x14\x00\x00\x00\x00\x00\x00\x72\x14\x00\x00\x00\x00\x00\x00"
132      "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00"
133      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
134      "\xfb\x12\x00\x00\x00\x00\x00\x00\x09\x13\x00\x00\x00\x00\x00\x00"
135      "\x0c\x13\x00\x00\x00\x00\x00\x00\xcb\x13\x00\x00\x00\x00\x00\x00"
136      "\x29\x14\x00\x00\x00\x00\x00\x00\x4e\x14\x00\x00\x00\x00\x00\x00"
137      "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00"
138      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
139      "\xfb\x12\x00\x00\x00\x00\x00\x00\x09\x13\x00\x00\x00\x00\x00\x00"
140      "\x67\x13\x00\x00\x00\x00\x00\x00\xcb\x13\x00\x00\x00\x00\x00\x00"
141      "\x9d\x14\x00\x00\x00\x00\x00\x00\xd5\x14\x00\x00\x00\x00\x00\x00"
142      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
143      "\x5f\x0b\x00\x00\x00\x00\x00\x00\x6c\x0b\x00\x00\x00\x00\x00\x00"
144      "\x7d\x0b\x00\x00\x00\x00\x00\x00\x7e\x0c\x00\x00\x00\x00\x00\x00"
145      "\x38\x0f\x00\x00\x00\x00\x00\x00\x5c\x0f\x00\x00\x00\x00\x00\x00"
146      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
147      "\x83\x0c\x00\x00\x00\x00\x00\x00\xfa\x0c\x00\x00\x00\x00\x00\x00"
148      "\xfd\x0d\x00\x00\x00\x00\x00\x00\xef\x0e\x00\x00\x00\x00\x00\x00"
149      "\x14\x0f\x00\x00\x00\x00\x00\x00\x38\x0f\x00\x00\x00\x00\x00\x00"
150      "\x9f\x0f\x00\x00\x00\x00\x00\x00\xac\x0f\x00\x00\x00\x00\x00\x00"
151      "\xdb\x0f\x00\x00\x00\x00\x00\x00\xff\x0f\x00\x00\x00\x00\x00\x00"
152      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
153      "\xfd\x0d\x00\x00\x00\x00\x00\x00\xd8\x0e\x00\x00\x00\x00\x00\x00"
154      "\x9f\x0f\x00\x00\x00\x00\x00\x00\xac\x0f\x00\x00\x00\x00\x00\x00"
155      "\xdb\x0f\x00\x00\x00\x00\x00\x00\xff\x0f\x00\x00\x00\x00\x00\x00"
156      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
157      "\xfa\x0c\x00\x00\x00\x00\x00\x00\xea\x0d\x00\x00\x00\x00\x00\x00"
158      "\xef\x0e\x00\x00\x00\x00\x00\x00\x14\x0f\x00\x00\x00\x00\x00\x00"
159      "\x5c\x0f\x00\x00\x00\x00\x00\x00\x9f\x0f\x00\x00\x00\x00\x00\x00"
160      "\xac\x0f\x00\x00\x00\x00\x00\x00\xdb\x0f\x00\x00\x00\x00\x00\x00"
161      "\xff\x0f\x00\x00\x00\x00\x00\x00\x2c\x10\x00\x00\x00\x00\x00\x00"
162      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
163      "\x60\x11\x00\x00\x00\x00\x00\x00\xd1\x16\x00\x00\x00\x00\x00\x00"
164      "\x40\x0b\x00\x00\x00\x00\x00\x00\x2c\x10\x00\x00\x00\x00\x00\x00"
165      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
166      "\x7a\x00\x00\x00\x00\x00\x00\x00\xb6\x00\x00\x00\x00\x00\x00\x00"
167      "\x9f\x01\x00\x00\x00\x00\x00\x00\xa7\x01\x00\x00\x00\x00\x00\x00"
168      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
169      "\x7a\x00\x00\x00\x00\x00\x00\x00\xa9\x00\x00\x00\x00\x00\x00\x00"
170      "\x9f\x01\x00\x00\x00\x00\x00\x00\xa7\x01\x00\x00\x00\x00\x00\x00"
171      "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"),
172     672,
173     ("\x78\x9c\x3b\x23\xc8\x00\x06\x57\x85\x21\xb4\x8c\x08\x84\x2e\x82"
174      "\xd2\x73\xa1\xf4\x55\x28\x8d\x0e\x7e\x0b\x41\x68\x4e\xa8\x7e\x1e"
175      "\x28\x7d\x1a\x4a\x6b\x42\xf5\xf9\x91\x69\x5e\x3a\x9a\x79\x84\xf4"
176      "\xc7\x73\x43\xe8\x1c\x28\x5d\x0b\xa5\xeb\x78\x20\xb4\x05\x3f\x84"
177      "\x8e\xe1\xc7\xae\xbf\x19\xaa\xee\x17\x94\xfe\xcb\x0b\xa1\xdf\xf3"
178      "\x41\x68\x11\x7e\x54\x73\xe6\x43\xe9\x35\x50\xfa\x36\x94\xfe\x8f"
179      "\xc3\x7c\x98\x79\x37\xf8\xc8\xd3\x0f\x73\xd7\x2b\x1c\xee\x8a\x21"
180      "\xd2\x5d\x3a\x02\xd8\xcd\x4f\x80\xa6\x87\x8b\x62\x10\xda\x81\x1b"
181      "\xbf\xfa\x2a\x28\xbd\x0d\x4a\xcf\x67\x84\xd0\xcb\x19\xf1\xab\x5f"
182      "\x49\xa4\x7a\x00\x48\x97\x29\xd4"),
183     152,
184   }
185 };
186 
187 /* Test the hand coded samples.  */
188 
189 static void
test_samples(struct backtrace_state * state)190 test_samples (struct backtrace_state *state)
191 {
192   size_t i;
193 
194   for (i = 0; i < sizeof tests / sizeof tests[0]; ++i)
195     {
196       char *p;
197       size_t v;
198       size_t j;
199       unsigned char *uncompressed;
200       size_t uncompressed_len;
201 
202       p = malloc (12 + tests[i].compressed_len);
203       memcpy (p, "ZLIB", 4);
204       v = tests[i].uncompressed_len;
205       if (v == 0)
206 	v = strlen (tests[i].uncompressed);
207       for (j = 0; j < 8; ++j)
208 	p[j + 4] = (v >> ((7 - j) * 8)) & 0xff;
209       memcpy (p + 12, tests[i].compressed, tests[i].compressed_len);
210       uncompressed = NULL;
211       uncompressed_len = 0;
212       if (!backtrace_uncompress_zdebug (state, (unsigned char *) p,
213 					tests[i].compressed_len + 12,
214 					error_callback_compress, NULL,
215 					&uncompressed, &uncompressed_len))
216 	{
217 	  fprintf (stderr, "test %s: uncompress failed\n", tests[i].name);
218 	  ++failures;
219 	}
220       else
221 	{
222 	  if (uncompressed_len != v)
223 	    {
224 	      fprintf (stderr,
225 		       "test %s: got uncompressed length %zu, want %zu\n",
226 		       tests[i].name, uncompressed_len, v);
227 	      ++failures;
228 	    }
229 	  else if (memcmp (tests[i].uncompressed, uncompressed, v) != 0)
230 	    {
231 	      size_t j;
232 
233 	      fprintf (stderr, "test %s: uncompressed data mismatch\n",
234 		       tests[i].name);
235 	      for (j = 0; j < v; ++j)
236 		if (tests[i].uncompressed[j] != uncompressed[j])
237 		  fprintf (stderr, "  %zu: got %#x want %#x\n", j,
238 			   uncompressed[j], tests[i].uncompressed[j]);
239 	      ++failures;
240 	    }
241 	  else
242 	    printf ("PASS: inflate %s\n", tests[i].name);
243 
244 	  backtrace_free (state, uncompressed, uncompressed_len,
245 			  error_callback_compress, NULL);
246 	}
247     }
248 }
249 
250 #ifdef HAVE_ZLIB
251 
252 /* Given a set of TRIALS timings, discard the lowest and highest
253    values and return the mean average of the rest.  */
254 
255 static size_t
average_time(const size_t * times,size_t trials)256 average_time (const size_t *times, size_t trials)
257 {
258   size_t imax;
259   size_t max;
260   size_t imin;
261   size_t min;
262   size_t i;
263   size_t sum;
264 
265   imin = 0;
266   imax = 0;
267   min = times[0];
268   max = times[0];
269   for (i = 1; i < trials; ++i)
270     {
271       if (times[i] < min)
272 	{
273 	  imin = i;
274 	  min = times[i];
275 	}
276       if (times[i] > max)
277 	{
278 	  imax = i;
279 	  max = times[i];
280 	}
281     }
282 
283   sum = 0;
284   for (i = 0; i < trials; ++i)
285     {
286       if (i != imax && i != imin)
287 	sum += times[i];
288     }
289   return sum / (trials - 2);
290 }
291 
292 #endif
293 
294 /* Test a larger text, if available.  */
295 
296 static void
test_large(struct backtrace_state * state ATTRIBUTE_UNUSED)297 test_large (struct backtrace_state *state ATTRIBUTE_UNUSED)
298 {
299 #ifdef HAVE_ZLIB
300   unsigned char *orig_buf;
301   size_t orig_bufsize;
302   size_t i;
303   char *compressed_buf;
304   size_t compressed_bufsize;
305   unsigned long compress_sizearg;
306   unsigned char *uncompressed_buf;
307   size_t uncompressed_bufsize;
308   int r;
309   clockid_t cid;
310   struct timespec ts1;
311   struct timespec ts2;
312   size_t ctime;
313   size_t ztime;
314   const size_t trials = 16;
315   size_t ctimes[16];
316   size_t ztimes[16];
317   static const char * const names[] = {
318     "Isaac.Newton-Opticks.txt",
319     "../libgo/go/testdata/Isaac.Newton-Opticks.txt",
320   };
321 
322   orig_buf = NULL;
323   orig_bufsize = 0;
324   uncompressed_buf = NULL;
325   compressed_buf = NULL;
326 
327   for (i = 0; i < sizeof names / sizeof names[0]; ++i)
328     {
329       size_t len;
330       char *namebuf;
331       FILE *e;
332       struct stat st;
333       char *rbuf;
334       size_t got;
335 
336       len = strlen (SRCDIR) + strlen (names[i]) + 2;
337       namebuf = malloc (len);
338       if (namebuf == NULL)
339 	{
340 	  perror ("malloc");
341 	  goto fail;
342 	}
343       snprintf (namebuf, len, "%s/%s", SRCDIR, names[i]);
344       e = fopen (namebuf, "r");
345       free (namebuf);
346       if (e == NULL)
347 	continue;
348       if (fstat (fileno (e), &st) < 0)
349 	{
350 	  perror ("fstat");
351 	  fclose (e);
352 	  continue;
353 	}
354       rbuf = malloc (st.st_size);
355       if (rbuf == NULL)
356 	{
357 	  perror ("malloc");
358 	  goto fail;
359 	}
360       got = fread (rbuf, 1, st.st_size, e);
361       fclose (e);
362       if (got > 0)
363 	{
364 	  orig_buf = (unsigned char *) rbuf;
365 	  orig_bufsize = got;
366 	  break;
367 	}
368       free (rbuf);
369     }
370 
371   if (orig_buf == NULL)
372     {
373       /* We couldn't find an input file.  */
374       printf ("UNSUPPORTED: inflate large\n");
375       return;
376     }
377 
378   compressed_bufsize = compressBound (orig_bufsize) + 12;
379   compressed_buf = malloc (compressed_bufsize);
380   if (compressed_buf == NULL)
381     {
382       perror ("malloc");
383       goto fail;
384     }
385 
386   compress_sizearg = compressed_bufsize - 12;
387   r = compress ((unsigned char *) compressed_buf + 12, &compress_sizearg,
388 		orig_buf, orig_bufsize);
389   if (r != Z_OK)
390     {
391       fprintf (stderr, "zlib compress failed: %d\n", r);
392       goto fail;
393     }
394 
395   compressed_bufsize = compress_sizearg + 12;
396 
397   /* Prepare the header that our library expects.  */
398   memcpy (compressed_buf, "ZLIB", 4);
399   for (i = 0; i < 8; ++i)
400     compressed_buf[i + 4] = (orig_bufsize >> ((7 - i) * 8)) & 0xff;
401 
402   uncompressed_buf = malloc (orig_bufsize);
403   if (uncompressed_buf == NULL)
404     {
405       perror ("malloc");
406       goto fail;
407     }
408   uncompressed_bufsize = orig_bufsize;
409 
410   if (!backtrace_uncompress_zdebug (state, (unsigned char *) compressed_buf,
411 				    compressed_bufsize,
412 				    error_callback_compress, NULL,
413 				    &uncompressed_buf, &uncompressed_bufsize))
414     {
415       fprintf (stderr, "inflate large: backtrace_uncompress_zdebug failed\n");
416       goto fail;
417     }
418 
419   if (uncompressed_bufsize != orig_bufsize)
420     {
421       fprintf (stderr,
422 	       "inflate large: got uncompressed length %zu, want %zu\n",
423 	       uncompressed_bufsize, orig_bufsize);
424       goto fail;
425     }
426 
427   if (memcmp (uncompressed_buf, orig_buf, uncompressed_bufsize) != 0)
428     {
429       fprintf (stderr, "inflate large: uncompressed data mismatch\n");
430       goto fail;
431     }
432 
433   printf ("PASS: inflate large\n");
434 
435   for (i = 0; i < trials; ++i)
436     {
437       unsigned long uncompress_sizearg;
438 
439       cid = ZLIB_CLOCK_GETTIME_ARG;
440       if (clock_gettime (cid, &ts1) < 0)
441 	{
442 	  if (errno == EINVAL)
443 	    return;
444 	  perror ("clock_gettime");
445 	  return;
446 	}
447 
448       if (!backtrace_uncompress_zdebug (state,
449 					(unsigned char *) compressed_buf,
450 					compressed_bufsize,
451 					error_callback_compress, NULL,
452 					&uncompressed_buf,
453 					&uncompressed_bufsize))
454 	{
455 	  fprintf (stderr,
456 		   ("inflate large: "
457 		    "benchmark backtrace_uncompress_zdebug failed\n"));
458 	  return;
459 	}
460 
461       if (clock_gettime (cid, &ts2) < 0)
462 	{
463 	  perror ("clock_gettime");
464 	  return;
465 	}
466 
467       ctime = (ts2.tv_sec - ts1.tv_sec) * 1000000000;
468       ctime += ts2.tv_nsec - ts1.tv_nsec;
469       ctimes[i] = ctime;
470 
471       if (clock_gettime (cid, &ts1) < 0)
472 	{
473 	  perror("clock_gettime");
474 	  return;
475 	}
476 
477       uncompress_sizearg = uncompressed_bufsize;
478       r = uncompress ((unsigned char *) uncompressed_buf, &uncompress_sizearg,
479 		      (unsigned char *) compressed_buf + 12,
480 		      compressed_bufsize - 12);
481 
482       if (clock_gettime (cid, &ts2) < 0)
483 	{
484 	  perror ("clock_gettime");
485 	  return;
486 	}
487 
488       if (r != Z_OK)
489 	{
490 	  fprintf (stderr,
491 		   "inflate large: benchmark zlib uncompress failed: %d\n",
492 		   r);
493 	  return;
494 	}
495 
496       ztime = (ts2.tv_sec - ts1.tv_sec) * 1000000000;
497       ztime += ts2.tv_nsec - ts1.tv_nsec;
498       ztimes[i] = ztime;
499     }
500 
501   /* Toss the highest and lowest times and average the rest.  */
502   ctime = average_time (ctimes, trials);
503   ztime = average_time (ztimes, trials);
504 
505   printf ("backtrace: %zu ns\n", ctime);
506   printf ("zlib     : %zu ns\n", ztime);
507   printf ("ratio    : %g\n", (double) ztime / (double) ctime);
508 
509   return;
510 
511  fail:
512   printf ("FAIL: inflate large\n");
513   ++failures;
514 
515   if (orig_buf != NULL)
516     free (orig_buf);
517   if (compressed_buf != NULL)
518     free (compressed_buf);
519   if (uncompressed_buf != NULL)
520     free (uncompressed_buf);
521 
522 #else /* !HAVE_ZLIB */
523 
524  printf ("UNSUPPORTED: inflate large\n");
525 
526 #endif /* !HAVE_ZLIB */
527 }
528 
529 int
main(int argc ATTRIBUTE_UNUSED,char ** argv)530 main (int argc ATTRIBUTE_UNUSED, char **argv)
531 {
532   struct backtrace_state *state;
533 
534   state = backtrace_create_state (argv[0], BACKTRACE_SUPPORTS_THREADS,
535 				  error_callback_create, NULL);
536 
537   test_samples (state);
538   test_large (state);
539 
540   exit (failures != 0 ? EXIT_FAILURE : EXIT_SUCCESS);
541 }
542