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
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
gettime(void)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
measure_unwind(int maxlevel,double * step)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
g1(int level,int maxlevel,double * step)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
f1(int level,int maxlevel,double * step)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
doit(const char * label)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
sum(void * buf,size_t size)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
measure_init(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
main(int argc,char ** argv)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