• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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