blob: 371445e6d49e0dd84b795a524d85240a92ead3ec [file] [log] [blame]
David Sehr4c16ac02016-03-17 13:51:42 -07001//===- 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
23extern "C" void *__libc_malloc(size_t size);
24
25namespace {
26// The Callers structure allocates memory, which would perturb the tracing.
27// InAllocatorFunction is true when we are tracing a new/malloc/...
28bool 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.
33using MallocMap = std::unordered_map<void *, uint64_t>;
34MallocMap *Callers;
35
36void *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.
50void *operator new(size_t size) {
51 void *caller = __builtin_return_address(0);
52 return internalAllocator(size, caller);
53}
54
55void *operator new[](size_t size) {
56 void *caller = __builtin_return_address(0);
57 return internalAllocator(size, caller);
58}
59
60extern "C" void *malloc(size_t size) {
61 void *caller = __builtin_return_address(0);
62 return internalAllocator(size, caller);
63}
64
65namespace Ice {
66
67LinuxMallocProfiling::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
77LinuxMallocProfiling::~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
100namespace Ice {
101
102LinuxMallocProfiling::LinuxMallocProfiling(size_t NumThreads, Ostream *Ls) {
103 (void)NumThreads;
104 (void)Ls;
105}
106
107LinuxMallocProfiling::~LinuxMallocProfiling() {}
108} // end of namespace Ice
109
110#endif // ALLOW_LINUX_MALLOC_PROFILE