| //===- 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 |