Home | History | Annotate | Download | only in src
      1 //===- subzero/src/LinuxMallocProfiling.cpp - malloc/new tracing  ---------===//
      2 //
      3 //                        The Subzero Code Generator
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 ///
     10 /// \file
     11 /// \brief malloc/new/...caller tracing.
     12 ///
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "LinuxMallocProfiling.h"
     16 
     17 #ifdef ALLOW_LINUX_MALLOC_PROFILE
     18 
     19 #include <dlfcn.h>
     20 #include <malloc.h>
     21 #include <unordered_map>
     22 
     23 extern "C" void *__libc_malloc(size_t size);
     24 
     25 namespace {
     26 // The Callers structure allocates memory, which would perturb the tracing.
     27 // InAllocatorFunction is true when we are tracing a new/malloc/...
     28 bool InAllocatorFunction = false;
     29 
     30 // Keep track of the number of times a particular call site address invoked an
     31 // allocator. NOTE: this is not thread safe, so the user must invoke with
     32 // --threads=0 to enable profiling.
     33 using MallocMap = std::unordered_map<void *, uint64_t>;
     34 MallocMap *Callers;
     35 
     36 void *internalAllocator(size_t size, void *caller) {
     37   if (Callers != nullptr && !InAllocatorFunction) {
     38     InAllocatorFunction = true;
     39     ++(*Callers)[caller];
     40     InAllocatorFunction = false;
     41   }
     42   return __libc_malloc(size);
     43 }
     44 } // end of anonymous namespace
     45 
     46 // new, new[], and malloc are all defined as weak symbols to allow them to be
     47 // overridden by user code.  This gives us a convenient place to hook allocation
     48 // tracking, to record the IP of the caller, which we get from the call to
     49 // __builtin_return_address.
     50 void *operator new(size_t size) {
     51   void *caller = __builtin_return_address(0);
     52   return internalAllocator(size, caller);
     53 }
     54 
     55 void *operator new[](size_t size) {
     56   void *caller = __builtin_return_address(0);
     57   return internalAllocator(size, caller);
     58 }
     59 
     60 extern "C" void *malloc(size_t size) {
     61   void *caller = __builtin_return_address(0);
     62   return internalAllocator(size, caller);
     63 }
     64 
     65 namespace Ice {
     66 
     67 LinuxMallocProfiling::LinuxMallocProfiling(size_t NumThreads, Ostream *Ls)
     68     : Ls(Ls) {
     69   if (NumThreads != 0) {
     70     *Ls << "NOTE: Malloc profiling is not thread safe. Use --threads=0 to "
     71            "enable.\n";
     72     return;
     73   }
     74   Callers = new MallocMap();
     75 }
     76 
     77 LinuxMallocProfiling::~LinuxMallocProfiling() {
     78   if (Callers == nullptr) {
     79     return;
     80   }
     81   for (const auto &C : *Callers) {
     82     Dl_info dli;
     83     dladdr(C.first, &dli);
     84 
     85     *Ls << C.second << " ";
     86     if (dli.dli_sname == NULL) {
     87       *Ls << C.first;
     88     } else {
     89       *Ls << dli.dli_sname;
     90     }
     91     *Ls << "\n";
     92   }
     93   delete Callers;
     94   Callers = nullptr;
     95 }
     96 } // end of namespace Ice
     97 
     98 #else // !ALLOW_LINUX_MALLOC_PROFILE
     99 
    100 namespace Ice {
    101 
    102 LinuxMallocProfiling::LinuxMallocProfiling(size_t NumThreads, Ostream *Ls) {
    103   (void)NumThreads;
    104   (void)Ls;
    105 }
    106 
    107 LinuxMallocProfiling::~LinuxMallocProfiling() {}
    108 } // end of namespace Ice
    109 
    110 #endif // ALLOW_LINUX_MALLOC_PROFILE
    111