CMS 3D CMS Logo

IgHookTrace.cc

Go to the documentation of this file.
00001 //<<<<<< INCLUDES                                                       >>>>>>
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 //<<<<<< PRIVATE DEFINES                                                >>>>>>
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 //<<<<<< PRIVATE CONSTANTS                                              >>>>>>
00028 //<<<<<< PRIVATE TYPES                                                  >>>>>>
00029 //<<<<<< PRIVATE VARIABLE DEFINITIONS                                   >>>>>>
00030 //<<<<<< PUBLIC VARIABLE DEFINITIONS                                    >>>>>>
00031 //<<<<<< CLASS STRUCTURE INITIALIZATION                                 >>>>>>
00032 //<<<<<< PRIVATE FUNCTION DEFINITIONS                                   >>>>>>
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 //<<<<<< PUBLIC FUNCTION DEFINITIONS                                    >>>>>>
00070 //<<<<<< MEMBER FUNCTION DEFINITIONS                                    >>>>>>
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     // The reason for the existence of this class is to allocate
00084     // memory directly using mmap() so we don't create calls to
00085     // malloc() and friends.  This is for two reasons: it must be
00086     // possible to use this in asynchronous signal handlers, and
00087     // calling malloc() in those is a really bad idea; and this is
00088     // meant to be used by profiling code and it's nicer to not
00089     // allocate memory in ways tracked by the profiler.
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 /* = 0 */)
00115 { return alloc ? alloc->allocate (n) : ::operator new (n); }
00116 
00117 IgHookTrace::CounterValue::CounterValue (Counter *counter,
00118                                          CounterValue *next /* = 0 */,
00119                                          unsigned long long value /* = 0 */)
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 /* = 0 */)
00179 { return alloc ? alloc->allocate (n) : ::operator new (n); }
00180 
00181 IgHookTrace::IgHookTrace (IgHookTrace *parent /* = 0 */, void *address /* = 0 */)
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         // Normal frame.
00261         frame           *ebp;
00262         void            *eip;
00263         // Signal frame stuff, put in here by kernel.
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     // Add fake entry to be compatible with other methods
00274     if (depth < nmax)
00275         addresses[depth++] = (void *) &IgHookTrace::stacktrace;
00276 
00277     // Top-most frame ends with null pointer; check the rest is reasonable
00278     while (depth < nmax && fp >= esp)
00279     {
00280         // Add this stack frame.  The return address is the
00281         // instruction immediately after the "call".  The call
00282         // instruction itself is 4 or 6 bytes; we guess 4.
00283         addresses[depth++] = (char *) fp->eip - 4;
00284 
00285         // Recognise signal frames.  We use two different methods
00286         // depending on the linux kernel version.
00287         //
00288         // For the "old" kernels / systems we check the instructions
00289         // at the caller's return address.  We take it to be a signal
00290         // frame if we find the signal return code sequence there
00291         // and the thread register context structure pointer:
00292         //
00293         //    mov $__NR_rt_sigreturn, %eax
00294         //    int 0x80
00295         //
00296         // For the "new" kernels / systems the operating system maps
00297         // a "vsyscall" page at a high address, and it may contain
00298         // either the above code, or use of the sysenter/sysexit
00299         // instructions.  We cannot poke at that page so we take the
00300         // the high address as an indication this is a signal frame.
00301         // (https://www.trilithium.com/johan/2005/08/linux-gate/)
00302         // (https://manugarg.googlepages.com/systemcallinlinux2_6.html)
00303         //
00304         // If we don't recognise the signal frame correctly here, we
00305         // lose one stack frame: signal delivery is not a call so
00306         // when the signal handler is entered, ebp still points to
00307         // what it was just before the signal.
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                 // __kernel_vsyscall stack on system call exit is
00322                 // [0] %ebp, [1] %edx, [2] %ecx, [3] return address.
00323                 if (depth < nmax)
00324                     addresses[depth++] = ((void **) fp)[3];
00325                 fp = fp->ebp;
00326 
00327                 // It seems the frame _above_ __kernel_syscall (the
00328                 // syscall implementation in libc, such as __mmap())
00329                 // is essentially frame-pointer-less, so we should
00330                 // find also the call above, but I don't know how
00331                 // to determine how many arguments the system call
00332                 // pushed on stack to call __kernel_syscall short
00333                 // of interpreting the DWARF unwind information :-(
00334                 // So we may lose one level of call stack here.
00335             }
00336         }
00337 
00338         // Otherwise it's a normal frame, process through frame pointer.
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     // Add fake entry to be compatible with other methods
00355     if (depth < nmax)
00356         addresses[depth++] = (void *) &IgHookTrace::stacktrace;
00357 
00358     while (depth < nmax && entry)
00359     {
00360         // LR points to the instruction after call, so step back.
00361         addresses[depth++] = entry - 4;
00362 
00363         // Check next one is a valid frame.
00364         frame *next = fp->sp;
00365         if (next <= fp || next <= sp)
00366             break;
00367 
00368         // Get and handle previous frame's call address.  Signal
00369         // frames are detected by being in sigtramp() and need
00370         // special handling.  The offset for pre-signal SP is
00371         // somewhat magic.
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         // FIXME: should counter know which of add()/max() we should use?
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 }

Generated on Tue Jun 9 17:38:08 2009 for CMSSW by  doxygen 1.5.4