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   unw_cursor_t cursor;
     62   unw_context_t uc;
     63   int ret, level = 0;
     64 
     65   unw_getcontext (&uc);
     66   if (unw_init_local (&cursor, &uc) < 0)
     67     panic ("unw_init_local() failed\n");
     68 
     69   start = gettime ();
     70 
     71   do
     72     {
     73       ret = unw_step (&cursor);
     74       if (ret < 0)
     75 	panic ("unw_step() failed\n");
     76       ++level;
     77     }
     78   while (ret > 0);
     79 
     80   stop = gettime ();
     81 
     82   if (level <= maxlevel)
     83     panic ("Unwound only %d levels, expected at least %d levels\n",
     84 	   level, maxlevel);
     85 
     86   *step = (stop - start) / (double) level;
     87   return 0;
     88 }
     89 
     90 static int f1 (int, int, double *);
     91 
     92 static int NOINLINE
     93 g1 (int level, int maxlevel, double *step)
     94 {
     95   if (level == maxlevel)
     96     return measure_unwind (maxlevel, step);
     97   else
     98     /* defeat last-call/sibcall optimization */
     99     return f1 (level + 1, maxlevel, step) + level;
    100 }
    101 
    102 static int NOINLINE
    103 f1 (int level, int maxlevel, double *step)
    104 {
    105   if (level == maxlevel)
    106     return measure_unwind (maxlevel, step);
    107   else
    108     /* defeat last-call/sibcall optimization */
    109     return g1 (level + 1, maxlevel, step) + level;
    110 }
    111 
    112 static void
    113 doit (const char *label)
    114 {
    115   double step, min_step, first_step, sum_step;
    116   int i;
    117 
    118   sum_step = first_step = 0.0;
    119   min_step = 1e99;
    120   for (i = 0; i < iterations; ++i)
    121     {
    122       f1 (0, maxlevel, &step);
    123 
    124       sum_step += step;
    125 
    126       if (step < min_step)
    127 	min_step = step;
    128 
    129       if (i == 0)
    130 	first_step = step;
    131     }
    132   printf ("%s: unw_step : 1st=%9.3f min=%9.3f avg=%9.3f nsec\n", label,
    133 	  1e9*first_step, 1e9*min_step, 1e9*sum_step/iterations);
    134 }
    135 
    136 static long
    137 sum (void *buf, size_t size)
    138 {
    139   long s = 0;
    140   char *cp = buf;
    141   size_t i;
    142 
    143   for (i = 0; i < size; i += 8)
    144     s += cp[i];
    145   return s;
    146 }
    147 
    148 static void
    149 measure_init (void)
    150 {
    151 # define N	100
    152 # define M	10	/* must be at least 2 to get steady-state */
    153   double stop, start, get_cold, get_warm, init_cold, init_warm, delta;
    154   struct
    155     {
    156       unw_cursor_t c;
    157       char padding[1024];	/* should be > 2 * max. cacheline size */
    158     }
    159   cursor[N];
    160   struct
    161     {
    162       unw_context_t uc;
    163       char padding[1024];	/* should be > 2 * max. cacheline size */
    164     }
    165   uc[N];
    166   int i, j;
    167 
    168   /* Run each test M times and take the minimum to filter out noise
    169      such dynamic linker resolving overhead, context-switches,
    170      page-in, cache, and TLB effects.  */
    171 
    172   get_cold = 1e99;
    173   for (j = 0; j < M; ++j)
    174     {
    175       dummy += sum (big, sizeof (big));			/* flush the cache */
    176       for (i = 0; i < N; ++i)
    177 	uc[i].padding[511] = i;		/* warm up the TLB */
    178       start = gettime ();
    179       for (i = 0; i < N; ++i)
    180 	unw_getcontext (&uc[i].uc);
    181       stop = gettime ();
    182       delta = (stop - start) / N;
    183       if (delta < get_cold)
    184 	get_cold = delta;
    185     }
    186 
    187   init_cold = 1e99;
    188   for (j = 0; j < M; ++j)
    189     {
    190       dummy += sum (big, sizeof (big));	/* flush cache */
    191       for (i = 0; i < N; ++i)
    192 	uc[i].padding[511] = i;		/* warm up the TLB */
    193       start = gettime ();
    194       for (i = 0; i < N; ++i)
    195 	unw_init_local (&cursor[i].c, &uc[i].uc);
    196       stop = gettime ();
    197       delta = (stop - start) / N;
    198       if (delta < init_cold)
    199 	init_cold = delta;
    200     }
    201 
    202   get_warm = 1e99;
    203   for (j = 0; j < M; ++j)
    204     {
    205       start = gettime ();
    206       for (i = 0; i < N; ++i)
    207 	unw_getcontext (&uc[0].uc);
    208       stop = gettime ();
    209       delta = (stop - start) / N;
    210       if (delta < get_warm)
    211 	get_warm = delta;
    212     }
    213 
    214   init_warm = 1e99;
    215   for (j = 0; j < M; ++j)
    216     {
    217       start = gettime ();
    218       for (i = 0; i < N; ++i)
    219 	unw_init_local (&cursor[0].c, &uc[0].uc);
    220       stop = gettime ();
    221       delta = (stop - start) / N;
    222       if (delta < init_warm)
    223 	init_warm = delta;
    224     }
    225 
    226   printf ("unw_getcontext : cold avg=%9.3f nsec, warm avg=%9.3f nsec\n",
    227 	  1e9 * get_cold, 1e9 * get_warm);
    228   printf ("unw_init_local : cold avg=%9.3f nsec, warm avg=%9.3f nsec\n",
    229 	  1e9 * init_cold, 1e9 * init_warm);
    230 }
    231 
    232 int
    233 main (int argc, char **argv)
    234 {
    235   struct rlimit rlim;
    236 
    237   rlim.rlim_cur = RLIM_INFINITY;
    238   rlim.rlim_max = RLIM_INFINITY;
    239   setrlimit (RLIMIT_STACK, &rlim);
    240 
    241   memset (big, 0xaa, sizeof (big));
    242 
    243   if (argc > 1)
    244     {
    245       maxlevel = atol (argv[1]);
    246       if (argc > 2)
    247 	iterations = atol (argv[2]);
    248     }
    249 
    250   measure_init ();
    251 
    252   unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_NONE);
    253   doit ("no cache        ");
    254 
    255   unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_GLOBAL);
    256   doit ("global cache    ");
    257 
    258   unw_set_caching_policy (unw_local_addr_space, UNW_CACHE_PER_THREAD);
    259   doit ("per-thread cache");
    260 
    261   return 0;
    262 }
    263