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