Home | History | Annotate | Download | only in tests
      1 /* libunwind - a platform-independent unwind library
      2    Copyright (C) 2003-2004 Hewlett-Packard Co
      3 	Contributed by David Mosberger-Tang <davidm (at) hpl.hp.com>
      4 
      5 Permission is hereby granted, free of charge, to any person obtaining
      6 a copy of this software and associated documentation files (the
      7 "Software"), to deal in the Software without restriction, including
      8 without limitation the rights to use, copy, modify, merge, publish,
      9 distribute, sublicense, and/or sell copies of the Software, and to
     10 permit persons to whom the Software is furnished to do so, subject to
     11 the following conditions:
     12 
     13 The above copyright notice and this permission notice shall be
     14 included in all copies or substantial portions of the Software.
     15 
     16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
     17 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
     18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
     19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
     20 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
     21 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
     22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  */
     23 
     24 #include <string.h>
     25 #include <stdio.h>
     26 #include <stdlib.h>
     27 #include <unistd.h>
     28 
     29 #include <libunwind.h>
     30 #include "compiler.h"
     31 
     32 #include <sys/resource.h>
     33 #include <sys/time.h>
     34 
     35 #define panic(args...)							  \
     36 	do { fprintf (stderr, args); exit (-1); } while (0)
     37 
     38 long dummy;
     39 
     40 static long iterations = 10000;
     41 static int maxlevel = 100;
     42 
     43 #define KB	1024
     44 #define MB	(1024*1024)
     45 
     46 static char big[64*MB];	/* should be >> max. cache size */
     47 
     48 static inline double
     49 gettime (void)
     50 {
     51   struct timeval tv;
     52 
     53   gettimeofday (&tv, NULL);
     54   return tv.tv_sec + 1e-6*tv.tv_usec;
     55 }
     56 
     57 static int NOINLINE
     58 measure_unwind (int maxlevel, double *step)
     59 {
     60   double stop, start;
     61   int level = 0;
     62   void *buffer[128];
     63 
     64   start = gettime ();
     65   level = unw_backtrace(buffer, 128);
     66   stop = gettime ();
     67 
     68   if (level <= maxlevel)
     69     panic ("Unwound only %d levels, expected at least %d levels\n",
     70 	   level, maxlevel);
     71 
     72   *step = (stop - start) / (double) level;
     73   return 0;
     74 }
     75 
     76 static int f1 (int, int, double *);
     77 
     78 static int NOINLINE
     79 g1 (int level, int maxlevel, double *step)
     80 {
     81   if (level == maxlevel)
     82     return measure_unwind (maxlevel, step);
     83   else
     84     /* defeat last-call/sibcall optimization */
     85     return f1 (level + 1, maxlevel, step) + level;
     86 }
     87 
     88 static int NOINLINE
     89 f1 (int level, int maxlevel, double *step)
     90 {
     91   if (level == maxlevel)
     92     return measure_unwind (maxlevel, step);
     93   else
     94     /* defeat last-call/sibcall optimization */
     95     return g1 (level + 1, maxlevel, step) + level;
     96 }
     97 
     98 static void
     99 doit (const char *label)
    100 {
    101   double step, min_step, first_step, sum_step;
    102   int i;
    103 
    104   sum_step = first_step = 0.0;
    105   min_step = 1e99;
    106   for (i = 0; i < iterations; ++i)
    107     {
    108       f1 (0, maxlevel, &step);
    109 
    110       sum_step += step;
    111 
    112       if (step < min_step)
    113 	min_step = step;
    114 
    115       if (i == 0)
    116 	first_step = step;
    117     }
    118   printf ("%s: unw_step : 1st=%9.3f min=%9.3f avg=%9.3f nsec\n", label,
    119 	  1e9*first_step, 1e9*min_step, 1e9*sum_step/iterations);
    120 }
    121 
    122 static long
    123 sum (void *buf, size_t size)
    124 {
    125   long s = 0;
    126   char *cp = buf;
    127   size_t i;
    128 
    129   for (i = 0; i < size; i += 8)
    130     s += cp[i];
    131   return s;
    132 }
    133 
    134 static void
    135 measure_init (void)
    136 {
    137 # define N	100
    138 # define M	10	/* must be at least 2 to get steady-state */
    139   double stop, start, get_cold, get_warm, init_cold, init_warm, delta;
    140   struct
    141     {
    142       unw_cursor_t c;
    143       char padding[1024];	/* should be > 2 * max. cacheline size */
    144     }
    145   cursor[N];
    146   struct
    147     {
    148       unw_context_t uc;
    149       char padding[1024];	/* should be > 2 * max. cacheline size */
    150     }
    151   uc[N];
    152   int i, j;
    153 
    154   /* Run each test M times and take the minimum to filter out noise
    155      such dynamic linker resolving overhead, context-switches,
    156      page-in, cache, and TLB effects.  */
    157 
    158   get_cold = 1e99;
    159   for (j = 0; j < M; ++j)
    160     {
    161       dummy += sum (big, sizeof (big));			/* flush the cache */
    162       for (i = 0; i < N; ++i)
    163 	uc[i].padding[511] = i;		/* warm up the TLB */
    164       start = gettime ();
    165       for (i = 0; i < N; ++i)
    166 	unw_getcontext (&uc[i].uc);
    167       stop = gettime ();
    168       delta = (stop - start) / N;
    169       if (delta < get_cold)
    170 	get_cold = delta;
    171     }
    172 
    173   init_cold = 1e99;
    174   for (j = 0; j < M; ++j)
    175     {
    176       dummy += sum (big, sizeof (big));	/* flush cache */
    177       for (i = 0; i < N; ++i)
    178 	uc[i].padding[511] = i;		/* warm up the TLB */
    179       start = gettime ();
    180       for (i = 0; i < N; ++i)
    181 	unw_init_local (&cursor[i].c, &uc[i].uc);
    182       stop = gettime ();
    183       delta = (stop - start) / N;
    184       if (delta < init_cold)
    185 	init_cold = delta;
    186     }
    187 
    188   get_warm = 1e99;
    189   for (j = 0; j < M; ++j)
    190     {
    191       start = gettime ();
    192       for (i = 0; i < N; ++i)
    193 	unw_getcontext (&uc[0].uc);
    194       stop = gettime ();
    195       delta = (stop - start) / N;
    196       if (delta < get_warm)
    197 	get_warm = delta;
    198     }
    199 
    200   init_warm = 1e99;
    201   for (j = 0; j < M; ++j)
    202     {
    203       start = gettime ();
    204       for (i = 0; i < N; ++i)
    205 	unw_init_local (&cursor[0].c, &uc[0].uc);
    206       stop = gettime ();
    207       delta = (stop - start) / N;
    208       if (delta < init_warm)
    209 	init_warm = delta;
    210     }
    211 
    212   printf ("unw_getcontext : cold avg=%9.3f nsec, warm avg=%9.3f nsec\n",
    213 	  1e9 * get_cold, 1e9 * get_warm);
    214   printf ("unw_init_local : cold avg=%9.3f nsec, warm avg=%9.3f nsec\n",
    215 	  1e9 * init_cold, 1e9 * init_warm);
    216 }
    217 
    218 int
    219 main (int argc, char **argv)
    220 {
    221   struct rlimit rlim;
    222 
    223   rlim.rlim_cur = RLIM_INFINITY;
    224   rlim.rlim_max = RLIM_INFINITY;
    225   setrlimit (RLIMIT_STACK, &rlim);
    226 
    227   memset (big, 0xaa, sizeof (big));
    228 
    229   if (argc > 1)
    230     {
    231       maxlevel = atol (argv[1]);
    232       if (argc > 2)
    233 	iterations = atol (argv[2]);
    234     }
    235 
    236   measure_init ();
    237 
    238   unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE);
    239   doit ("no cache        ");
    240 
    241   unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL);
    242   doit ("global cache    ");
    243 
    244   unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD);
    245   doit ("per-thread cache");
    246 
    247   return 0;
    248 }
    249