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