1/* libunwind - a platform-independent unwind library
2   Copyright (C) 2003-2004 Hewlett-Packard Co
3	Contributed by David Mosberger-Tang <davidm@hpl.hp.com>
4
5Permission is hereby granted, free of charge, to any person obtaining
6a copy of this software and associated documentation files (the
7"Software"), to deal in the Software without restriction, including
8without limitation the rights to use, copy, modify, merge, publish,
9distribute, sublicense, and/or sell copies of the Software, and to
10permit persons to whom the Software is furnished to do so, subject to
11the following conditions:
12
13The above copyright notice and this permission notice shall be
14included in all copies or substantial portions of the Software.
15
16THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22WITH 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
38long dummy;
39
40static long iterations = 10000;
41static int maxlevel = 100;
42
43#define KB	1024
44#define MB	(1024*1024)
45
46static char big[64*MB];	/* should be >> max. cache size */
47
48static inline double
49gettime (void)
50{
51  struct timeval tv;
52
53  gettimeofday (&tv, NULL);
54  return tv.tv_sec + 1e-6*tv.tv_usec;
55}
56
57static int NOINLINE
58measure_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
76static int f1 (int, int, double *);
77
78static int NOINLINE
79g1 (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
88static int NOINLINE
89f1 (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
98static void
99doit (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
122static long
123sum (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
134static void
135measure_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
218int
219main (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