//===- subzero/src/LinuxMallocProfiling.cpp - malloc/new tracing ---------===// // // The Subzero Code Generator // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// /// /// \file /// \brief malloc/new/...caller tracing. /// //===----------------------------------------------------------------------===// #include "LinuxMallocProfiling.h" #ifdef ALLOW_LINUX_MALLOC_PROFILE #include <dlfcn.h> #include <malloc.h> #include <unordered_map> extern "C" void *__libc_malloc(size_t size); namespace { // The Callers structure allocates memory, which would perturb the tracing. // InAllocatorFunction is true when we are tracing a new/malloc/... bool InAllocatorFunction = false; // Keep track of the number of times a particular call site address invoked an // allocator. NOTE: this is not thread safe, so the user must invoke with // --threads=0 to enable profiling. using MallocMap = std::unordered_map<void *, uint64_t>; MallocMap *Callers; void *internalAllocator(size_t size, void *caller) { if (Callers != nullptr && !InAllocatorFunction) { InAllocatorFunction = true; ++(*Callers)[caller]; InAllocatorFunction = false; } return __libc_malloc(size); } } // end of anonymous namespace // new, new[], and malloc are all defined as weak symbols to allow them to be // overridden by user code. This gives us a convenient place to hook allocation // tracking, to record the IP of the caller, which we get from the call to // __builtin_return_address. void *operator new(size_t size) { void *caller = __builtin_return_address(0); return internalAllocator(size, caller); } void *operator new[](size_t size) { void *caller = __builtin_return_address(0); return internalAllocator(size, caller); } extern "C" void *malloc(size_t size) { void *caller = __builtin_return_address(0); return internalAllocator(size, caller); } namespace Ice { LinuxMallocProfiling::LinuxMallocProfiling(size_t NumThreads, Ostream *Ls) : Ls(Ls) { if (NumThreads != 0) { *Ls << "NOTE: Malloc profiling is not thread safe. Use --threads=0 to " "enable.\n"; return; } Callers = new MallocMap(); } LinuxMallocProfiling::~LinuxMallocProfiling() { if (Callers == nullptr) { return; } for (const auto &C : *Callers) { Dl_info dli; dladdr(C.first, &dli); *Ls << C.second << " "; if (dli.dli_sname == NULL) { *Ls << C.first; } else { *Ls << dli.dli_sname; } *Ls << "\n"; } delete Callers; Callers = nullptr; } } // end of namespace Ice #else // !ALLOW_LINUX_MALLOC_PROFILE namespace Ice { LinuxMallocProfiling::LinuxMallocProfiling(size_t NumThreads, Ostream *Ls) { (void)NumThreads; (void)Ls; } LinuxMallocProfiling::~LinuxMallocProfiling() {} } // end of namespace Ice #endif // ALLOW_LINUX_MALLOC_PROFILE