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  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
90static int f1 (int, int, double *);
91
92static int NOINLINE
93g1 (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
102static int NOINLINE
103f1 (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
112static void
113doit (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
136static long
137sum (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
148static void
149measure_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
232int
233main (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