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