1 /* xztest.c -- Test for libbacktrace LZMA decoder.
2    Copyright (C) 2020-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 <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 
56 typedef int xclockid_t;
57 
58 static int
xclock_gettime(xclockid_t id ATTRIBUTE_UNUSED,struct timespec * ts ATTRIBUTE_UNUSED)59 xclock_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 
81 struct 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 
92 static void
error_callback_compress(void * vdata ATTRIBUTE_UNUSED,const char * msg,int errnum)93 error_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 
103 static 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 
139 static void
test_samples(struct backtrace_state * state)140 test_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 (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 
201 static size_t
average_time(const size_t * times,size_t trials)202 average_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 
242 static void
test_large(struct backtrace_state * state ATTRIBUTE_UNUSED)243 test_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 
496 int
main(int argc ATTRIBUTE_UNUSED,char ** argv)497 main (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