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