1 // Copyright 2020 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "aemu/base/memory/MemoryTracker.h"
16
17 #define AEMU_TCMALLOC_ENABLED 0
18
19 #ifndef AEMU_TCMALLOC_ENABLED
20 #error "Need to know whether we enabled TCMalloc!"
21 #endif
22
23 #if AEMU_TCMALLOC_ENABLED && defined(__linux__)
24 #include <malloc_extension_c.h>
25 #include <malloc_hook.h>
26
27 #include <execinfo.h>
28 #include <libunwind.h>
29 #include <algorithm>
30 #include <set>
31 #include <sstream>
32 #include <vector>
33 #endif
34
35 #define E(fmt, ...) fprintf(stderr, "%s: " fmt "\n", __func__, ##__VA_ARGS__);
36
37 namespace android {
38 namespace base {
39
40 struct FuncRange {
41 std::string mName;
42 intptr_t mAddr;
43 size_t mLength;
44 MemoryTracker::MallocStats mStats;
45 };
46
47 class MemoryTracker::Impl {
48 public:
49 #if AEMU_TCMALLOC_ENABLED && defined(__linux__)
Impl()50 Impl()
51 : mData([](const FuncRange* a, const FuncRange* b) {
52 return a->mAddr + a->mLength < b->mAddr + b->mLength;
53 }) {}
54
addToGroup(const std::string & group,const std::string & func)55 bool addToGroup(const std::string& group, const std::string& func) {
56 std::string key = group + func;
57 if (mRegisterFuncs.find(group + func) != mRegisterFuncs.end()) {
58 return false;
59 }
60
61 unw_cursor_t cursor;
62 unw_context_t uc;
63 unw_getcontext(&uc);
64 unw_init_local(&cursor, &uc);
65 /* We step for 3 times because, in <GLcommon/GLESmacros.h>, addToGroup()
66 * is invoked by MEM_TRACE_IF using std::call_once().
67 * This approach to reduce unnecessary checks and potential
68 * race conditions. The typical backtrace looks like:
69 * 10c121b2c (android::base::MemoryTracker::addToGroup()
70 * 10c0cebb6 (void
71 * std::__1::__call_once_proxy<std::__1::tuple<>>(void*)) 7fff4e9f336e
72 * (std::__1::__call_once(unsigned long volatile&, void*, void
73 * (*)(void*))) 10c0cc489 (eglClientWaitSyncKHR)+89 where
74 * eglClientWaitSyncKHR is what we want to register. Do not change the
75 * number of times unw_step() unless the implementation for MEM_TRACE_IF
76 * in <GLcommon/GLESmacros.h> has changed.
77 */
78 unw_step(&cursor);
79 unw_step(&cursor);
80 unw_step(&cursor);
81 unw_proc_info_t info;
82 if (0 != unw_get_proc_info(&cursor, &info)) {
83 return false;
84 }
85 mRegisterFuncs.insert(key);
86 mData.insert(new FuncRange{key, (intptr_t)info.start_ip,
87 info.end_ip - info.start_ip});
88
89 return true;
90 }
91
newHook(const void * ptr,size_t size)92 void newHook(const void* ptr, size_t size) {
93 size = std::max(size, MallocExtension_GetAllocatedSize(ptr));
94 void* results[kStackTraceLimit];
95 #if defined(__linux__)
96 int ret = unw_backtrace(results, kStackTraceLimit);
97 #endif
98 for (int i = 5; i < ret; i++) {
99 intptr_t addr = (intptr_t)results[i];
100 /*
101 * The invariant is that all registered functions will be sorted
102 * based on the starting address. We can use binary search to test
103 * if an address falls within a specific function and reduce the
104 * look up time.
105 */
106 FuncRange func{"", addr, 0};
107 auto it = mData.upper_bound(&func);
108 if (it != mData.end() && (*it)->mAddr <= addr) {
109 (*it)->mStats.mAllocated += size;
110 (*it)->mStats.mLive += size;
111 break;
112 }
113 }
114 }
115
deleteHook(const void * ptr)116 void deleteHook(const void* ptr) {
117 size_t size = MallocExtension_GetAllocatedSize(ptr);
118 void* results[kStackTraceLimit];
119 #if defined(__linux__)
120 int ret = unw_backtrace(results, kStackTraceLimit);
121 #endif
122 for (int i = 5; i < ret; i++) {
123 intptr_t addr = (intptr_t)results[i];
124 FuncRange func{"", addr, 0};
125 auto it = mData.upper_bound(&func);
126 if (it != mData.end() && (*it)->mAddr <= addr) {
127 (*it)->mStats.mLive -= size;
128 break;
129 }
130 }
131 }
132
printUsage(int verbosity)133 std::string printUsage(int verbosity) {
134 std::stringstream ss;
135 if (!enabled){
136 ss << "Memory tracker not enabled\n";
137 return ss.str();
138 }
139
140 auto stats = getUsage("EMUGL");
141 ss << "EMUGL memory allocated: ";
142 ss << (float)stats->mAllocated.load() / 1048576.0f;
143 ss << "mb live: ";
144 ss << (float)stats->mLive.load() / 1048576.0f;
145 ss << "mb\n";
146 // If verbose, return the memory stats for each registered functions
147 // sorted by live memory
148 if (verbosity) {
149 std::vector<FuncRange*> allStats;
150 for (auto it : mData) {
151 allStats.push_back(it);
152 }
153 std::sort(allStats.begin(), allStats.end(),
154 [](FuncRange* a, FuncRange* b) {
155 return std::abs(a->mStats.mLive) >
156 std::abs(b->mStats.mLive);
157 });
158 for (auto it : allStats) {
159 ss << it->mName + " memory allocated: ";
160 ss << (float)it->mStats.mAllocated.load() / 1048576.0f;
161 ss << "mb live: ";
162 ss << (float)it->mStats.mLive.load() / 1048576.0f;
163 ss << "mb\n";
164 }
165 }
166
167 return ss.str();
168 }
169
new_hook(const void * ptr,size_t size)170 static void new_hook(const void* ptr, size_t size) {
171 MemoryTracker::get()->mImpl->newHook(ptr, size);
172 }
173
delete_hook(const void * ptr)174 static void delete_hook(const void* ptr) {
175 MemoryTracker::get()->mImpl->deleteHook(ptr);
176 }
177
start()178 void start() {
179 if (!MallocHook::AddNewHook(&new_hook) ||
180 !MallocHook::AddDeleteHook(&delete_hook)) {
181 E("Failed to add malloc hooks.");
182 enabled = false;
183 } else {
184 enabled = true;
185 }
186 }
187
stop()188 void stop() {
189 if (enabled) {
190 MallocHook::RemoveNewHook(&new_hook);
191 MallocHook::RemoveDeleteHook(&delete_hook);
192 }
193 }
194
isEnabled()195 bool isEnabled() { return enabled; }
196
getUsage(const std::string & group)197 std::unique_ptr<MallocStats> getUsage(const std::string& group) {
198 std::unique_ptr<MallocStats> ms(new MallocStats());
199 for (auto& it : mData) {
200 if (it->mName.compare(0, group.size(), group) == 0) {
201 ms->mAllocated += it->mStats.mAllocated.load();
202 ms->mLive += it->mStats.mLive.load();
203 }
204 }
205 return std::move(ms);
206 }
207
208 private:
209 bool enabled = false;
210 std::set<FuncRange*, bool (*)(const FuncRange*, const FuncRange*)> mData;
211 std::set<std::string> mRegisterFuncs;
212 static const int kStackTraceLimit = 32;
213 #else
214 bool addToGroup(std::string group, std::string func) {
215 (void)group;
216 (void)func;
217 return false;
218 }
219
220 void start() { E("Not implemented"); }
221
222 void stop() { E("Not implemented"); }
223
224 std::string printUsage(int verbosity) {
225 return "<memory usage tracker not implemented>";
226 }
227
228 bool isEnabled() { return false; }
229
230 std::unique_ptr<MallocStats> getUsage(const std::string& group) {
231 return nullptr;
232 }
233
234 #endif
235 };
236
MemoryTracker()237 MemoryTracker::MemoryTracker() : mImpl(new MemoryTracker::Impl()) {}
238
addToGroup(const std::string & group,const std::string & func)239 bool MemoryTracker::addToGroup(const std::string& group,
240 const std::string& func) {
241 return mImpl->addToGroup(group, func);
242 }
243
printUsage(int verbosity)244 std::string MemoryTracker::printUsage(int verbosity) {
245 return mImpl->printUsage(verbosity);
246 }
247
start()248 void MemoryTracker::start() {
249 mImpl->start();
250 }
251
stop()252 void MemoryTracker::stop() {
253 mImpl->stop();
254 }
255
isEnabled()256 bool MemoryTracker::isEnabled() {
257 return mImpl->isEnabled();
258 }
259
getUsage(const std::string & group)260 std::unique_ptr<MemoryTracker::MallocStats> MemoryTracker::getUsage(
261 const std::string& group) {
262 return mImpl->getUsage(group);
263 }
264
265 #if defined(__linux__)
sMemoryTracker()266 static MemoryTracker* sMemoryTracker() {
267 static MemoryTracker* m = new MemoryTracker;
268 return m;
269 }
270 #endif
271
272 // static
get()273 MemoryTracker* MemoryTracker::get() {
274 #if defined(__linux__)
275 return sMemoryTracker();
276 #else
277 return nullptr;
278 #endif
279 }
280
281 } // namespace base
282 } // namespace android
283