00001
00002
00003 #include "IgTools/IgHook/interface/IgHookTrace.h"
00004 #include <cstdlib>
00005 #include <cstdio>
00006 #include <dlfcn.h>
00007 #include <unistd.h>
00008 #include <sys/mman.h>
00009 #if __linux
00010 # include <execinfo.h>
00011 # include <ucontext.h>
00012 # include <sys/syscall.h>
00013 #endif
00014 #if __APPLE__
00015 extern "C" void _sigtramp (void);
00016 #endif
00017
00018
00019
00020 #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)
00021 # define HAVE_UNWIND_BACKTRACE 1
00022 #endif
00023 #if !defined MAP_ANONYMOUS && defined MAP_ANON
00024 # define MAP_ANONYMOUS MAP_ANON
00025 #endif
00026
00027
00028
00029
00030
00031
00032
00033
00034 #if HAVE_UNWIND_BACKTRACE
00035 struct IgHookTraceArgs { void **array; int count; int size; };
00036 extern "C" {
00037 typedef unsigned _Unwind_Ptr __attribute__((__mode__(__pointer__)));
00038 struct _Unwind_Context;
00039 enum _Unwind_Reason_Code
00040 {
00041 _URC_NO_REASON = 0,
00042 _URC_FOREIGN_EXCEPTION_CAUGHT = 1,
00043 _URC_FATAL_PHASE2_ERROR = 2,
00044 _URC_FATAL_PHASE1_ERROR = 3,
00045 _URC_NORMAL_STOP = 4,
00046 _URC_END_OF_STACK = 5,
00047 _URC_HANDLER_FOUND = 6,
00048 _URC_INSTALL_CONTEXT = 7,
00049 _URC_CONTINUE_UNWIND = 8
00050 };
00051
00052 typedef _Unwind_Reason_Code (*_Unwind_Trace_Fn) (_Unwind_Context *, void *);
00053 _Unwind_Reason_Code _Unwind_Backtrace (_Unwind_Trace_Fn, void *);
00054 _Unwind_Ptr _Unwind_GetIP (_Unwind_Context *);
00055 }
00056
00057 static _Unwind_Reason_Code
00058 GCCBackTrace (_Unwind_Context *context, void *arg)
00059 {
00060 IgHookTraceArgs *args = (IgHookTraceArgs *) arg;
00061 if (args->count >= 0 && args->count < args->size)
00062 args->array [args->count++] = (void *) _Unwind_GetIP (context);
00063 else
00064 return _URC_END_OF_STACK;
00065 return _URC_NO_REASON;
00066 }
00067 #endif
00068
00069
00070
00071
00075 IgHookTraceAlloc::IgHookTraceAlloc (void)
00076 : m_pool (0),
00077 m_left (0)
00078 {}
00079
00080 void *
00081 IgHookTraceAlloc::allocate (size_t bytes)
00082 {
00083
00084
00085
00086
00087
00088
00089
00090 if (m_left < bytes)
00091 {
00092 size_t psize = getpagesize ();
00093 size_t hunk = psize * 20;
00094 if (hunk < bytes) hunk = (hunk + psize - 1) & ~(psize-1);
00095 void *addr = mmap (0, hunk, PROT_READ | PROT_WRITE,
00096 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
00097 if (addr == MAP_FAILED)
00098 return 0;
00099
00100 m_pool = addr;
00101 m_left = hunk;
00102 }
00103
00104 void *ptr = m_pool;
00105 m_pool = (char *) m_pool + bytes;
00106 m_left -= bytes;
00107 return ptr;
00108 }
00109
00113 void *
00114 IgHookTrace::CounterValue::operator new (size_t n, IgHookTraceAlloc *alloc )
00115 { return alloc ? alloc->allocate (n) : ::operator new (n); }
00116
00117 IgHookTrace::CounterValue::CounterValue (Counter *counter,
00118 CounterValue *next ,
00119 unsigned long long value )
00120 : m_counter (counter),
00121 m_next (next),
00122 m_value (value),
00123 m_count (0)
00124 {}
00125
00126 IgHookTrace::Counter *
00127 IgHookTrace::CounterValue::counter (void)
00128 { return m_counter; }
00129
00130 IgHookTrace::CounterValue *
00131 IgHookTrace::CounterValue::next (void)
00132 { return m_next; }
00133
00134 unsigned long long
00135 IgHookTrace::CounterValue::value (void)
00136 { return m_value; }
00137
00138 unsigned long long
00139 IgHookTrace::CounterValue::count (void)
00140 { return m_count; }
00141
00142 unsigned long long
00143 IgHookTrace::CounterValue::tick (void)
00144 { ++m_count; return ++m_value; }
00145
00146 unsigned long long
00147 IgHookTrace::CounterValue::untick (void)
00148 { --m_count; return --m_value; }
00149
00150 unsigned long long
00151 IgHookTrace::CounterValue::add (unsigned long long value)
00152 { ++m_count; return m_value += value; }
00153
00154 unsigned long long
00155 IgHookTrace::CounterValue::sub (unsigned long long value)
00156 { --m_count; return m_value -= value; }
00157
00158 unsigned long long
00159 IgHookTrace::CounterValue::max (unsigned long long value)
00160 { ++m_count; if (m_value < value) m_value = value; return m_value; }
00161
00162 unsigned long long
00163 IgHookTrace::CounterValue::add (CounterValue &x)
00164 { m_count += x.m_count; m_value += x.m_value; return m_value; }
00165
00166 unsigned long long
00167 IgHookTrace::CounterValue::sub (CounterValue &x)
00168 { m_count -= x.m_count; m_value -= x.m_value; return m_value; }
00169
00170 unsigned long long
00171 IgHookTrace::CounterValue::max (CounterValue &x)
00172 { m_count += x.m_count; if (m_value < x.m_value) m_value = x.m_value; return m_value; }
00173
00177 void *
00178 IgHookTrace::operator new (size_t n, IgHookTraceAlloc *alloc )
00179 { return alloc ? alloc->allocate (n) : ::operator new (n); }
00180
00181 IgHookTrace::IgHookTrace (IgHookTrace *parent , void *address )
00182 : m_alloc (parent ? parent->m_alloc : new IgHookTraceAlloc),
00183 m_parent (parent),
00184 m_next (parent ? parent->m_children : 0),
00185 m_children (0),
00186 m_address (address),
00187 m_counters (0)
00188 { if (m_parent) m_parent->m_children = this; }
00189
00190 IgHookTrace *
00191 IgHookTrace::parent (void)
00192 { return m_parent; }
00193
00194 IgHookTrace *
00195 IgHookTrace::next (void)
00196 { return m_next; }
00197
00198 void *
00199 IgHookTrace::address (void)
00200 { return m_address; }
00201
00202 bool
00203 IgHookTrace::symbol (void *address,
00204 const char *&sym,
00205 const char *&lib,
00206 int &offset,
00207 int &liboffset)
00208 {
00209 sym = lib = 0;
00210 offset = 0;
00211 liboffset = (unsigned long) address;
00212
00213 Dl_info info;
00214 if (dladdr (address, &info))
00215 {
00216 if (info.dli_fname && info.dli_fname [0])
00217 lib = info.dli_fname;
00218
00219 if (info.dli_fbase)
00220 liboffset = (unsigned long) address - (unsigned long) info.dli_fbase;
00221
00222 if (info.dli_saddr)
00223 offset = (unsigned long) address - (unsigned long) info.dli_saddr;
00224
00225 if (info.dli_sname && info.dli_sname [0])
00226 sym = info.dli_sname;
00227
00228 return true;
00229 }
00230
00231 return false;
00232 }
00233
00234 bool
00235 IgHookTrace::symbol (const char *&sym, const char *&lib, int &offset, int &liboffset)
00236 { return symbol (m_address, sym, lib, offset, liboffset); }
00237
00238 void *
00239 IgHookTrace::tosymbol (void *address)
00240 {
00241 Dl_info info;
00242 return (dladdr (address, &info)
00243 && info.dli_fname
00244 && info.dli_fname [0]
00245 && info.dli_saddr)
00246 ? info.dli_saddr : address;
00247 }
00248
00249 int
00250 IgHookTrace::stacktrace (void **addresses, int nmax)
00251 {
00252 #if __linux && __i386
00253 # if ! __x86_64__
00254 # define PROBABLY_VSYSCALL_PAGE 0xffff0000
00255 # else
00256 # define PROBABLY_VSYSCALL_PAGE 0xffffffff00000000
00257 # endif
00258 struct frame
00259 {
00260
00261 frame *ebp;
00262 void *eip;
00263
00264 int signo;
00265 siginfo_t *info;
00266 ucontext_t *ctx;
00267 };
00268 register frame *ebp __asm__ ("ebp");
00269 register frame *esp __asm__ ("esp");
00270 frame *fp = ebp;
00271 int depth = 0;
00272
00273
00274 if (depth < nmax)
00275 addresses[depth++] = (void *) &IgHookTrace::stacktrace;
00276
00277
00278 while (depth < nmax && fp >= esp)
00279 {
00280
00281
00282
00283 addresses[depth++] = (char *) fp->eip - 4;
00284
00285
00286
00287
00288
00289
00290
00291
00292
00293
00294
00295
00296
00297
00298
00299
00300
00301
00302
00303
00304
00305
00306
00307
00308 unsigned char *insn = (unsigned char *) fp->eip;
00309 if (insn
00310 && insn[0] == 0xb8 && insn[1] == __NR_rt_sigreturn
00311 && insn[5] == 0xcd && insn[6] == 0x80
00312 && fp->ctx)
00313 {
00314 void *retip = (void *) fp->ctx->uc_mcontext.gregs [REG_EIP];
00315 if (depth < nmax)
00316 addresses[depth++] = retip;
00317
00318 fp = (frame *) fp->ctx->uc_mcontext.gregs [REG_EBP];
00319 if (fp && (unsigned long) retip > PROBABLY_VSYSCALL_PAGE)
00320 {
00321
00322
00323 if (depth < nmax)
00324 addresses[depth++] = ((void **) fp)[3];
00325 fp = fp->ebp;
00326
00327
00328
00329
00330
00331
00332
00333
00334
00335 }
00336 }
00337
00338
00339 else
00340 fp = fp->ebp;
00341 }
00342
00343 return depth;
00344 #elif __APPLE__ && __ppc__
00345 struct frame { frame *sp; void *cr; char *lr; };
00346 char *sigtramplow = (char *) &_sigtramp;
00347 char *sigtramphi = (char *) sigtramplow + 256;
00348 register frame *sp __asm__ ("sp");
00349 register char *lr __asm__ ("lr");
00350 frame *fp = sp;
00351 char *entry = lr;
00352 int depth = 0;
00353
00354
00355 if (depth < nmax)
00356 addresses[depth++] = (void *) &IgHookTrace::stacktrace;
00357
00358 while (depth < nmax && entry)
00359 {
00360
00361 addresses[depth++] = entry - 4;
00362
00363
00364 frame *next = fp->sp;
00365 if (next <= fp || next <= sp)
00366 break;
00367
00368
00369
00370
00371
00372 if (entry >= sigtramplow && entry <= sigtramphi)
00373 {
00374 fp = *(frame **) ((char *) next + 156);
00375 entry = *(char **) ((char *) next + 144);
00376 }
00377 else
00378 {
00379 fp = next;
00380 entry = fp->lr;
00381 }
00382 }
00383
00384 return depth;
00385 #elif __linux
00386 return backtrace (addresses, nmax);
00387 #elif HAVE_UNWIND_BACKTRACE
00388 if (nmax >= 1)
00389 {
00390 IgHookTraceArgs args = { addresses, 0, nmax };
00391 _Unwind_Backtrace (&GCCBackTrace, &args);
00392
00393 if (args.count > 1 && args.array [args.count-1] == 0)
00394 args.count--;
00395
00396 return args.count;
00397 }
00398 return 0;
00399 #else
00400 return 0;
00401 #endif
00402 }
00403
00404 IgHookTrace *
00405 IgHookTrace::children (void)
00406 { return m_children; }
00407
00408 IgHookTrace *
00409 IgHookTrace::child (void *address)
00410 {
00411 for (IgHookTrace *kid = m_children; kid; kid = kid->m_next)
00412 if (kid->m_address == address)
00413 return kid;
00414
00415 return new (m_alloc) IgHookTrace (this, address);
00416 }
00417
00418 IgHookTrace::CounterValue *
00419 IgHookTrace::counters (void)
00420 { return m_counters; }
00421
00422 IgHookTrace::CounterValue *
00423 IgHookTrace::counter (Counter *id)
00424 {
00425 for (CounterValue *val = m_counters; val; val = val->next ())
00426 if (val->counter () == id)
00427 return val;
00428
00429 m_counters = new (m_alloc) CounterValue (id, m_counters);
00430 return m_counters;
00431 }
00432
00433 void
00434 IgHookTrace::merge (IgHookTrace *other)
00435 {
00436 for (CounterValue *val = other->m_counters; val; val = val->next ())
00437
00438 counter (val->counter ())->add (*val);
00439
00440 for (IgHookTrace *kid = other->m_children; kid; kid = kid->m_next)
00441 child (kid->m_address)->merge (kid);
00442 }