| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 1 | /* | 
 | 2 |  * Copyright (C) 2016 The Android Open Source Project | 
 | 3 |  * | 
 | 4 |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
 | 5 |  * you may not use this file except in compliance with the License. | 
 | 6 |  * You may obtain a copy of the License at | 
 | 7 |  * | 
 | 8 |  *      http://www.apache.org/licenses/LICENSE-2.0 | 
 | 9 |  * | 
 | 10 |  * Unless required by applicable law or agreed to in writing, software | 
 | 11 |  * distributed under the License is distributed on an "AS IS" BASIS, | 
 | 12 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
 | 13 |  * See the License for the specific language governing permissions and | 
 | 14 |  * limitations under the License. | 
 | 15 |  */ | 
 | 16 |  | 
 | 17 | #include <inttypes.h> | 
| Colin Cross | a9939e9 | 2017-06-21 13:13:00 -0700 | [diff] [blame] | 18 | #include <string.h> | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 19 |  | 
 | 20 | #include <functional> | 
 | 21 | #include <iomanip> | 
 | 22 | #include <mutex> | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 23 | #include <sstream> | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 24 | #include <string> | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 25 | #include <unordered_map> | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 26 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 27 | #include <android-base/macros.h> | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 28 | #include <backtrace.h> | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 29 |  | 
 | 30 | #include "Allocator.h" | 
| Colin Cross | f572b91 | 2017-06-20 18:07:29 -0700 | [diff] [blame] | 31 | #include "Binder.h" | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 32 | #include "HeapWalker.h" | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 33 | #include "Leak.h" | 
| Colin Cross | 8e8f34c | 2016-03-02 17:53:39 -0800 | [diff] [blame] | 34 | #include "LeakFolding.h" | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 35 | #include "LeakPipe.h" | 
 | 36 | #include "ProcessMappings.h" | 
 | 37 | #include "PtracerThread.h" | 
 | 38 | #include "ScopedDisableMalloc.h" | 
 | 39 | #include "Semaphore.h" | 
 | 40 | #include "ThreadCapture.h" | 
 | 41 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 42 | #include "bionic.h" | 
 | 43 | #include "log.h" | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 44 | #include "memunreachable/memunreachable.h" | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 45 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 46 | using namespace std::chrono_literals; | 
 | 47 |  | 
| Colin Cross | a9939e9 | 2017-06-21 13:13:00 -0700 | [diff] [blame] | 48 | namespace android { | 
 | 49 |  | 
 | 50 | const size_t Leak::contents_length; | 
 | 51 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 52 | class MemUnreachable { | 
 | 53 |  public: | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 54 |   MemUnreachable(pid_t pid, Allocator<void> allocator) | 
 | 55 |       : pid_(pid), allocator_(allocator), heap_walker_(allocator_) {} | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 56 |   bool CollectAllocations(const allocator::vector<ThreadInfo>& threads, | 
| Colin Cross | f572b91 | 2017-06-20 18:07:29 -0700 | [diff] [blame] | 57 |                           const allocator::vector<Mapping>& mappings, | 
 | 58 |                           const allocator::vector<uintptr_t>& refs); | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 59 |   bool GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, size_t* num_leaks, | 
 | 60 |                             size_t* leak_bytes); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 61 |   size_t Allocations() { return heap_walker_.Allocations(); } | 
 | 62 |   size_t AllocationBytes() { return heap_walker_.AllocationBytes(); } | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 63 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 64 |  private: | 
 | 65 |   bool ClassifyMappings(const allocator::vector<Mapping>& mappings, | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 66 |                         allocator::vector<Mapping>& heap_mappings, | 
 | 67 |                         allocator::vector<Mapping>& anon_mappings, | 
 | 68 |                         allocator::vector<Mapping>& globals_mappings, | 
 | 69 |                         allocator::vector<Mapping>& stack_mappings); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 70 |   DISALLOW_COPY_AND_ASSIGN(MemUnreachable); | 
 | 71 |   pid_t pid_; | 
 | 72 |   Allocator<void> allocator_; | 
 | 73 |   HeapWalker heap_walker_; | 
 | 74 | }; | 
 | 75 |  | 
 | 76 | static void HeapIterate(const Mapping& heap_mapping, | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 77 |                         const std::function<void(uintptr_t, size_t)>& func) { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 78 |   malloc_iterate(heap_mapping.begin, heap_mapping.end - heap_mapping.begin, | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 79 |                  [](uintptr_t base, size_t size, void* arg) { | 
 | 80 |                    auto f = reinterpret_cast<const std::function<void(uintptr_t, size_t)>*>(arg); | 
 | 81 |                    (*f)(base, size); | 
 | 82 |                  }, | 
 | 83 |                  const_cast<void*>(reinterpret_cast<const void*>(&func))); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 84 | } | 
 | 85 |  | 
 | 86 | bool MemUnreachable::CollectAllocations(const allocator::vector<ThreadInfo>& threads, | 
| Colin Cross | f572b91 | 2017-06-20 18:07:29 -0700 | [diff] [blame] | 87 |                                         const allocator::vector<Mapping>& mappings, | 
 | 88 |                                         const allocator::vector<uintptr_t>& refs) { | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 89 |   MEM_ALOGI("searching process %d for allocations", pid_); | 
| Colin Cross | 3ca1976 | 2018-11-28 17:01:59 -0800 | [diff] [blame] | 90 |  | 
 | 91 |   for (auto it = mappings.begin(); it != mappings.end(); it++) { | 
 | 92 |     heap_walker_.Mapping(it->begin, it->end); | 
 | 93 |   } | 
 | 94 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 95 |   allocator::vector<Mapping> heap_mappings{mappings}; | 
 | 96 |   allocator::vector<Mapping> anon_mappings{mappings}; | 
 | 97 |   allocator::vector<Mapping> globals_mappings{mappings}; | 
 | 98 |   allocator::vector<Mapping> stack_mappings{mappings}; | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 99 |   if (!ClassifyMappings(mappings, heap_mappings, anon_mappings, globals_mappings, stack_mappings)) { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 100 |     return false; | 
 | 101 |   } | 
 | 102 |  | 
 | 103 |   for (auto it = heap_mappings.begin(); it != heap_mappings.end(); it++) { | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 104 |     MEM_ALOGV("Heap mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name); | 
 | 105 |     HeapIterate(*it, | 
 | 106 |                 [&](uintptr_t base, size_t size) { heap_walker_.Allocation(base, base + size); }); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 107 |   } | 
 | 108 |  | 
 | 109 |   for (auto it = anon_mappings.begin(); it != anon_mappings.end(); it++) { | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 110 |     MEM_ALOGV("Anon mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 111 |     heap_walker_.Allocation(it->begin, it->end); | 
 | 112 |   } | 
 | 113 |  | 
 | 114 |   for (auto it = globals_mappings.begin(); it != globals_mappings.end(); it++) { | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 115 |     MEM_ALOGV("Globals mapping %" PRIxPTR "-%" PRIxPTR " %s", it->begin, it->end, it->name); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 116 |     heap_walker_.Root(it->begin, it->end); | 
 | 117 |   } | 
 | 118 |  | 
 | 119 |   for (auto thread_it = threads.begin(); thread_it != threads.end(); thread_it++) { | 
 | 120 |     for (auto it = stack_mappings.begin(); it != stack_mappings.end(); it++) { | 
 | 121 |       if (thread_it->stack.first >= it->begin && thread_it->stack.first <= it->end) { | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 122 |         MEM_ALOGV("Stack %" PRIxPTR "-%" PRIxPTR " %s", thread_it->stack.first, it->end, it->name); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 123 |         heap_walker_.Root(thread_it->stack.first, it->end); | 
 | 124 |       } | 
 | 125 |     } | 
 | 126 |     heap_walker_.Root(thread_it->regs); | 
 | 127 |   } | 
 | 128 |  | 
| Colin Cross | f572b91 | 2017-06-20 18:07:29 -0700 | [diff] [blame] | 129 |   heap_walker_.Root(refs); | 
 | 130 |  | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 131 |   MEM_ALOGI("searching done"); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 132 |  | 
 | 133 |   return true; | 
 | 134 | } | 
 | 135 |  | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 136 | bool MemUnreachable::GetUnreachableMemory(allocator::vector<Leak>& leaks, size_t limit, | 
 | 137 |                                           size_t* num_leaks, size_t* leak_bytes) { | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 138 |   MEM_ALOGI("sweeping process %d for unreachable memory", pid_); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 139 |   leaks.clear(); | 
 | 140 |  | 
| Colin Cross | 8e8f34c | 2016-03-02 17:53:39 -0800 | [diff] [blame] | 141 |   if (!heap_walker_.DetectLeaks()) { | 
 | 142 |     return false; | 
 | 143 |   } | 
 | 144 |  | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 145 |   allocator::vector<Range> leaked1{allocator_}; | 
 | 146 |   heap_walker_.Leaked(leaked1, 0, num_leaks, leak_bytes); | 
 | 147 |  | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 148 |   MEM_ALOGI("sweeping done"); | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 149 |  | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 150 |   MEM_ALOGI("folding related leaks"); | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 151 |  | 
| Colin Cross | 8e8f34c | 2016-03-02 17:53:39 -0800 | [diff] [blame] | 152 |   LeakFolding folding(allocator_, heap_walker_); | 
 | 153 |   if (!folding.FoldLeaks()) { | 
 | 154 |     return false; | 
 | 155 |   } | 
 | 156 |  | 
 | 157 |   allocator::vector<LeakFolding::Leak> leaked{allocator_}; | 
 | 158 |  | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 159 |   if (!folding.Leaked(leaked, num_leaks, leak_bytes)) { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 160 |     return false; | 
 | 161 |   } | 
 | 162 |  | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 163 |   allocator::unordered_map<Leak::Backtrace, Leak*> backtrace_map{allocator_}; | 
 | 164 |  | 
 | 165 |   // Prevent reallocations of backing memory so we can store pointers into it | 
 | 166 |   // in backtrace_map. | 
 | 167 |   leaks.reserve(leaked.size()); | 
 | 168 |  | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 169 |   for (auto& it : leaked) { | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 170 |     leaks.emplace_back(); | 
 | 171 |     Leak* leak = &leaks.back(); | 
 | 172 |  | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 173 |     ssize_t num_backtrace_frames = malloc_backtrace( | 
 | 174 |         reinterpret_cast<void*>(it.range.begin), leak->backtrace.frames, leak->backtrace.max_frames); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 175 |     if (num_backtrace_frames > 0) { | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 176 |       leak->backtrace.num_frames = num_backtrace_frames; | 
 | 177 |  | 
 | 178 |       auto inserted = backtrace_map.emplace(leak->backtrace, leak); | 
 | 179 |       if (!inserted.second) { | 
 | 180 |         // Leak with same backtrace already exists, drop this one and | 
 | 181 |         // increment similar counts on the existing one. | 
 | 182 |         leaks.pop_back(); | 
 | 183 |         Leak* similar_leak = inserted.first->second; | 
 | 184 |         similar_leak->similar_count++; | 
 | 185 |         similar_leak->similar_size += it.range.size(); | 
 | 186 |         similar_leak->similar_referenced_count += it.referenced_count; | 
 | 187 |         similar_leak->similar_referenced_size += it.referenced_size; | 
 | 188 |         similar_leak->total_size += it.range.size(); | 
 | 189 |         similar_leak->total_size += it.referenced_size; | 
 | 190 |         continue; | 
 | 191 |       } | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 192 |     } | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 193 |  | 
 | 194 |     leak->begin = it.range.begin; | 
 | 195 |     leak->size = it.range.size(); | 
 | 196 |     leak->referenced_count = it.referenced_count; | 
 | 197 |     leak->referenced_size = it.referenced_size; | 
 | 198 |     leak->total_size = leak->size + leak->referenced_size; | 
 | 199 |     memcpy(leak->contents, reinterpret_cast<void*>(it.range.begin), | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 200 |            std::min(leak->size, Leak::contents_length)); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 201 |   } | 
 | 202 |  | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 203 |   MEM_ALOGI("folding done"); | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 204 |  | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 205 |   std::sort(leaks.begin(), leaks.end(), | 
 | 206 |             [](const Leak& a, const Leak& b) { return a.total_size > b.total_size; }); | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 207 |  | 
 | 208 |   if (leaks.size() > limit) { | 
 | 209 |     leaks.resize(limit); | 
 | 210 |   } | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 211 |  | 
 | 212 |   return true; | 
 | 213 | } | 
 | 214 |  | 
 | 215 | static bool has_prefix(const allocator::string& s, const char* prefix) { | 
 | 216 |   int ret = s.compare(0, strlen(prefix), prefix); | 
 | 217 |   return ret == 0; | 
 | 218 | } | 
 | 219 |  | 
 | 220 | bool MemUnreachable::ClassifyMappings(const allocator::vector<Mapping>& mappings, | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 221 |                                       allocator::vector<Mapping>& heap_mappings, | 
 | 222 |                                       allocator::vector<Mapping>& anon_mappings, | 
 | 223 |                                       allocator::vector<Mapping>& globals_mappings, | 
 | 224 |                                       allocator::vector<Mapping>& stack_mappings) { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 225 |   heap_mappings.clear(); | 
 | 226 |   anon_mappings.clear(); | 
 | 227 |   globals_mappings.clear(); | 
 | 228 |   stack_mappings.clear(); | 
 | 229 |  | 
 | 230 |   allocator::string current_lib{allocator_}; | 
 | 231 |  | 
 | 232 |   for (auto it = mappings.begin(); it != mappings.end(); it++) { | 
 | 233 |     if (it->execute) { | 
 | 234 |       current_lib = it->name; | 
 | 235 |       continue; | 
 | 236 |     } | 
 | 237 |  | 
 | 238 |     if (!it->read) { | 
 | 239 |       continue; | 
 | 240 |     } | 
 | 241 |  | 
 | 242 |     const allocator::string mapping_name{it->name, allocator_}; | 
 | 243 |     if (mapping_name == "[anon:.bss]") { | 
 | 244 |       // named .bss section | 
 | 245 |       globals_mappings.emplace_back(*it); | 
 | 246 |     } else if (mapping_name == current_lib) { | 
 | 247 |       // .rodata or .data section | 
 | 248 |       globals_mappings.emplace_back(*it); | 
 | 249 |     } else if (mapping_name == "[anon:libc_malloc]") { | 
 | 250 |       // named malloc mapping | 
 | 251 |       heap_mappings.emplace_back(*it); | 
| Joel Fernandes | ed59ff4 | 2018-08-24 11:57:57 -0700 | [diff] [blame] | 252 |     } else if (has_prefix(mapping_name, "[anon:dalvik-")) { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 253 |       // named dalvik heap mapping | 
 | 254 |       globals_mappings.emplace_back(*it); | 
 | 255 |     } else if (has_prefix(mapping_name, "[stack")) { | 
 | 256 |       // named stack mapping | 
 | 257 |       stack_mappings.emplace_back(*it); | 
 | 258 |     } else if (mapping_name.size() == 0) { | 
 | 259 |       globals_mappings.emplace_back(*it); | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 260 |     } else if (has_prefix(mapping_name, "[anon:") && | 
 | 261 |                mapping_name != "[anon:leak_detector_malloc]") { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 262 |       // TODO(ccross): it would be nice to treat named anonymous mappings as | 
 | 263 |       // possible leaks, but naming something in a .bss or .data section makes | 
 | 264 |       // it impossible to distinguish them from mmaped and then named mappings. | 
 | 265 |       globals_mappings.emplace_back(*it); | 
 | 266 |     } | 
 | 267 |   } | 
 | 268 |  | 
 | 269 |   return true; | 
 | 270 | } | 
 | 271 |  | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 272 | template <typename T> | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 273 | static inline const char* plural(T val) { | 
 | 274 |   return (val == 1) ? "" : "s"; | 
 | 275 | } | 
 | 276 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 277 | bool GetUnreachableMemory(UnreachableMemoryInfo& info, size_t limit) { | 
 | 278 |   int parent_pid = getpid(); | 
 | 279 |   int parent_tid = gettid(); | 
 | 280 |  | 
 | 281 |   Heap heap; | 
 | 282 |  | 
 | 283 |   Semaphore continue_parent_sem; | 
 | 284 |   LeakPipe pipe; | 
 | 285 |  | 
 | 286 |   PtracerThread thread{[&]() -> int { | 
 | 287 |     ///////////////////////////////////////////// | 
 | 288 |     // Collection thread | 
 | 289 |     ///////////////////////////////////////////// | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 290 |     MEM_ALOGI("collecting thread info for process %d...", parent_pid); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 291 |  | 
 | 292 |     ThreadCapture thread_capture(parent_pid, heap); | 
 | 293 |     allocator::vector<ThreadInfo> thread_info(heap); | 
 | 294 |     allocator::vector<Mapping> mappings(heap); | 
| Colin Cross | f572b91 | 2017-06-20 18:07:29 -0700 | [diff] [blame] | 295 |     allocator::vector<uintptr_t> refs(heap); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 296 |  | 
 | 297 |     // ptrace all the threads | 
 | 298 |     if (!thread_capture.CaptureThreads()) { | 
| Colin Cross | de42af0 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 299 |       continue_parent_sem.Post(); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 300 |       return 1; | 
 | 301 |     } | 
 | 302 |  | 
 | 303 |     // collect register contents and stacks | 
 | 304 |     if (!thread_capture.CapturedThreadInfo(thread_info)) { | 
| Colin Cross | de42af0 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 305 |       continue_parent_sem.Post(); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 306 |       return 1; | 
 | 307 |     } | 
 | 308 |  | 
 | 309 |     // snapshot /proc/pid/maps | 
 | 310 |     if (!ProcessMappings(parent_pid, mappings)) { | 
| Colin Cross | de42af0 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 311 |       continue_parent_sem.Post(); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 312 |       return 1; | 
 | 313 |     } | 
 | 314 |  | 
| Colin Cross | f572b91 | 2017-06-20 18:07:29 -0700 | [diff] [blame] | 315 |     if (!BinderReferences(refs)) { | 
 | 316 |       continue_parent_sem.Post(); | 
 | 317 |       return 1; | 
 | 318 |     } | 
 | 319 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 320 |     // malloc must be enabled to call fork, at_fork handlers take the same | 
 | 321 |     // locks as ScopedDisableMalloc.  All threads are paused in ptrace, so | 
 | 322 |     // memory state is still consistent.  Unfreeze the original thread so it | 
 | 323 |     // can drop the malloc locks, it will block until the collection thread | 
 | 324 |     // exits. | 
 | 325 |     thread_capture.ReleaseThread(parent_tid); | 
 | 326 |     continue_parent_sem.Post(); | 
 | 327 |  | 
 | 328 |     // fork a process to do the heap walking | 
 | 329 |     int ret = fork(); | 
 | 330 |     if (ret < 0) { | 
 | 331 |       return 1; | 
 | 332 |     } else if (ret == 0) { | 
 | 333 |       ///////////////////////////////////////////// | 
 | 334 |       // Heap walker process | 
 | 335 |       ///////////////////////////////////////////// | 
 | 336 |       // Examine memory state in the child using the data collected above and | 
 | 337 |       // the CoW snapshot of the process memory contents. | 
 | 338 |  | 
 | 339 |       if (!pipe.OpenSender()) { | 
 | 340 |         _exit(1); | 
 | 341 |       } | 
 | 342 |  | 
 | 343 |       MemUnreachable unreachable{parent_pid, heap}; | 
 | 344 |  | 
| Colin Cross | f572b91 | 2017-06-20 18:07:29 -0700 | [diff] [blame] | 345 |       if (!unreachable.CollectAllocations(thread_info, mappings, refs)) { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 346 |         _exit(2); | 
 | 347 |       } | 
 | 348 |       size_t num_allocations = unreachable.Allocations(); | 
 | 349 |       size_t allocation_bytes = unreachable.AllocationBytes(); | 
 | 350 |  | 
 | 351 |       allocator::vector<Leak> leaks{heap}; | 
 | 352 |  | 
 | 353 |       size_t num_leaks = 0; | 
 | 354 |       size_t leak_bytes = 0; | 
 | 355 |       bool ok = unreachable.GetUnreachableMemory(leaks, limit, &num_leaks, &leak_bytes); | 
 | 356 |  | 
 | 357 |       ok = ok && pipe.Sender().Send(num_allocations); | 
 | 358 |       ok = ok && pipe.Sender().Send(allocation_bytes); | 
 | 359 |       ok = ok && pipe.Sender().Send(num_leaks); | 
 | 360 |       ok = ok && pipe.Sender().Send(leak_bytes); | 
 | 361 |       ok = ok && pipe.Sender().SendVector(leaks); | 
 | 362 |  | 
 | 363 |       if (!ok) { | 
 | 364 |         _exit(3); | 
 | 365 |       } | 
 | 366 |  | 
 | 367 |       _exit(0); | 
 | 368 |     } else { | 
 | 369 |       // Nothing left to do in the collection thread, return immediately, | 
 | 370 |       // releasing all the captured threads. | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 371 |       MEM_ALOGI("collection thread done"); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 372 |       return 0; | 
 | 373 |     } | 
 | 374 |   }}; | 
 | 375 |  | 
 | 376 |   ///////////////////////////////////////////// | 
 | 377 |   // Original thread | 
 | 378 |   ///////////////////////////////////////////// | 
 | 379 |  | 
 | 380 |   { | 
 | 381 |     // Disable malloc to get a consistent view of memory | 
 | 382 |     ScopedDisableMalloc disable_malloc; | 
 | 383 |  | 
 | 384 |     // Start the collection thread | 
 | 385 |     thread.Start(); | 
 | 386 |  | 
 | 387 |     // Wait for the collection thread to signal that it is ready to fork the | 
 | 388 |     // heap walker process. | 
| Colin Cross | de42af0 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 389 |     continue_parent_sem.Wait(30s); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 390 |  | 
 | 391 |     // Re-enable malloc so the collection thread can fork. | 
 | 392 |   } | 
 | 393 |  | 
 | 394 |   // Wait for the collection thread to exit | 
 | 395 |   int ret = thread.Join(); | 
 | 396 |   if (ret != 0) { | 
 | 397 |     return false; | 
 | 398 |   } | 
 | 399 |  | 
 | 400 |   // Get a pipe from the heap walker process.  Transferring a new pipe fd | 
 | 401 |   // ensures no other forked processes can have it open, so when the heap | 
 | 402 |   // walker process dies the remote side of the pipe will close. | 
 | 403 |   if (!pipe.OpenReceiver()) { | 
 | 404 |     return false; | 
 | 405 |   } | 
 | 406 |  | 
 | 407 |   bool ok = true; | 
 | 408 |   ok = ok && pipe.Receiver().Receive(&info.num_allocations); | 
 | 409 |   ok = ok && pipe.Receiver().Receive(&info.allocation_bytes); | 
 | 410 |   ok = ok && pipe.Receiver().Receive(&info.num_leaks); | 
 | 411 |   ok = ok && pipe.Receiver().Receive(&info.leak_bytes); | 
 | 412 |   ok = ok && pipe.Receiver().ReceiveVector(info.leaks); | 
 | 413 |   if (!ok) { | 
 | 414 |     return false; | 
 | 415 |   } | 
 | 416 |  | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 417 |   MEM_ALOGI("unreachable memory detection done"); | 
 | 418 |   MEM_ALOGE("%zu bytes in %zu allocation%s unreachable out of %zu bytes in %zu allocation%s", | 
 | 419 |             info.leak_bytes, info.num_leaks, plural(info.num_leaks), info.allocation_bytes, | 
 | 420 |             info.num_allocations, plural(info.num_allocations)); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 421 |   return true; | 
 | 422 | } | 
 | 423 |  | 
 | 424 | std::string Leak::ToString(bool log_contents) const { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 425 |   std::ostringstream oss; | 
 | 426 |  | 
 | 427 |   oss << "  " << std::dec << size; | 
| Colin Cross | de42af0 | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 428 |   oss << " bytes unreachable at "; | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 429 |   oss << std::hex << begin; | 
 | 430 |   oss << std::endl; | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 431 |   if (referenced_count > 0) { | 
 | 432 |     oss << std::dec; | 
 | 433 |     oss << "   referencing " << referenced_size << " unreachable bytes"; | 
 | 434 |     oss << " in " << referenced_count << " allocation" << plural(referenced_count); | 
 | 435 |     oss << std::endl; | 
 | 436 |   } | 
 | 437 |   if (similar_count > 0) { | 
 | 438 |     oss << std::dec; | 
 | 439 |     oss << "   and " << similar_size << " similar unreachable bytes"; | 
 | 440 |     oss << " in " << similar_count << " allocation" << plural(similar_count); | 
 | 441 |     oss << std::endl; | 
 | 442 |     if (similar_referenced_count > 0) { | 
 | 443 |       oss << "   referencing " << similar_referenced_size << " unreachable bytes"; | 
 | 444 |       oss << " in " << similar_referenced_count << " allocation" << plural(similar_referenced_count); | 
 | 445 |       oss << std::endl; | 
 | 446 |     } | 
 | 447 |   } | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 448 |  | 
 | 449 |   if (log_contents) { | 
 | 450 |     const int bytes_per_line = 16; | 
 | 451 |     const size_t bytes = std::min(size, contents_length); | 
 | 452 |  | 
 | 453 |     if (bytes == size) { | 
 | 454 |       oss << "   contents:" << std::endl; | 
 | 455 |     } else { | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 456 |       oss << "   first " << bytes << " bytes of contents:" << std::endl; | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 457 |     } | 
 | 458 |  | 
 | 459 |     for (size_t i = 0; i < bytes; i += bytes_per_line) { | 
 | 460 |       oss << "   " << std::hex << begin + i << ": "; | 
 | 461 |       size_t j; | 
 | 462 |       oss << std::setfill('0'); | 
 | 463 |       for (j = i; j < bytes && j < i + bytes_per_line; j++) { | 
 | 464 |         oss << std::setw(2) << static_cast<int>(contents[j]) << " "; | 
 | 465 |       } | 
 | 466 |       oss << std::setfill(' '); | 
 | 467 |       for (; j < i + bytes_per_line; j++) { | 
 | 468 |         oss << "   "; | 
 | 469 |       } | 
 | 470 |       for (j = i; j < bytes && j < i + bytes_per_line; j++) { | 
 | 471 |         char c = contents[j]; | 
 | 472 |         if (c < ' ' || c >= 0x7f) { | 
 | 473 |           c = '.'; | 
 | 474 |         } | 
 | 475 |         oss << c; | 
 | 476 |       } | 
 | 477 |       oss << std::endl; | 
 | 478 |     } | 
 | 479 |   } | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 480 |   if (backtrace.num_frames > 0) { | 
 | 481 |     oss << backtrace_string(backtrace.frames, backtrace.num_frames); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 482 |   } | 
 | 483 |  | 
 | 484 |   return oss.str(); | 
 | 485 | } | 
 | 486 |  | 
 | 487 | std::string UnreachableMemoryInfo::ToString(bool log_contents) const { | 
 | 488 |   std::ostringstream oss; | 
 | 489 |   oss << "  " << leak_bytes << " bytes in "; | 
| Colin Cross | 7a22e81 | 2016-03-04 16:36:12 -0800 | [diff] [blame] | 490 |   oss << num_leaks << " unreachable allocation" << plural(num_leaks); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 491 |   oss << std::endl; | 
| Colin Cross | 11185af | 2016-03-04 16:37:02 -0800 | [diff] [blame] | 492 |   oss << "  ABI: '" ABI_STRING "'" << std::endl; | 
 | 493 |   oss << std::endl; | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 494 |  | 
 | 495 |   for (auto it = leaks.begin(); it != leaks.end(); it++) { | 
| Colin Cross | a83881e | 2017-06-22 10:50:05 -0700 | [diff] [blame] | 496 |     oss << it->ToString(log_contents); | 
 | 497 |     oss << std::endl; | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 498 |   } | 
 | 499 |  | 
 | 500 |   return oss.str(); | 
 | 501 | } | 
 | 502 |  | 
| Colin Cross | ca71f17 | 2018-05-14 14:55:39 -0700 | [diff] [blame] | 503 | UnreachableMemoryInfo::~UnreachableMemoryInfo() { | 
 | 504 |   // Clear the memory that holds the leaks, otherwise the next attempt to | 
 | 505 |   // detect leaks may find the old data (for example in the jemalloc tcache) | 
 | 506 |   // and consider all the leaks to be referenced. | 
 | 507 |   memset(leaks.data(), 0, leaks.capacity() * sizeof(Leak)); | 
 | 508 |  | 
 | 509 |   std::vector<Leak> tmp; | 
 | 510 |   leaks.swap(tmp); | 
 | 511 |  | 
 | 512 |   // Disable and re-enable malloc to flush the jemalloc tcache to make sure | 
 | 513 |   // there are no copies of the leaked pointer addresses there. | 
 | 514 |   malloc_disable(); | 
 | 515 |   malloc_enable(); | 
 | 516 | } | 
 | 517 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 518 | std::string GetUnreachableMemoryString(bool log_contents, size_t limit) { | 
 | 519 |   UnreachableMemoryInfo info; | 
 | 520 |   if (!GetUnreachableMemory(info, limit)) { | 
| Colin Cross | 72d3881 | 2017-06-13 16:41:58 -0700 | [diff] [blame] | 521 |     return "Failed to get unreachable memory\n" | 
 | 522 |            "If you are trying to get unreachable memory from a system app\n" | 
 | 523 |            "(like com.android.systemui), disable selinux first using\n" | 
 | 524 |            "setenforce 0\n"; | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 525 |   } | 
 | 526 |  | 
 | 527 |   return info.ToString(log_contents); | 
 | 528 | } | 
 | 529 |  | 
| Colin Cross | a9939e9 | 2017-06-21 13:13:00 -0700 | [diff] [blame] | 530 | }  // namespace android | 
 | 531 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 532 | bool LogUnreachableMemory(bool log_contents, size_t limit) { | 
| Colin Cross | a9939e9 | 2017-06-21 13:13:00 -0700 | [diff] [blame] | 533 |   android::UnreachableMemoryInfo info; | 
 | 534 |   if (!android::GetUnreachableMemory(info, limit)) { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 535 |     return false; | 
 | 536 |   } | 
 | 537 |  | 
 | 538 |   for (auto it = info.leaks.begin(); it != info.leaks.end(); it++) { | 
| Christopher Ferris | 47dea71 | 2017-05-03 17:34:29 -0700 | [diff] [blame] | 539 |     MEM_ALOGE("%s", it->ToString(log_contents).c_str()); | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 540 |   } | 
 | 541 |   return true; | 
 | 542 | } | 
 | 543 |  | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 544 | bool NoLeaks() { | 
| Colin Cross | a9939e9 | 2017-06-21 13:13:00 -0700 | [diff] [blame] | 545 |   android::UnreachableMemoryInfo info; | 
 | 546 |   if (!android::GetUnreachableMemory(info, 0)) { | 
| Colin Cross | 7add50d | 2016-01-14 15:35:40 -0800 | [diff] [blame] | 547 |     return false; | 
 | 548 |   } | 
 | 549 |  | 
 | 550 |   return info.num_leaks == 0; | 
 | 551 | } |