• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  *  * Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in
12  *    the documentation and/or other materials provided with the
13  *    distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22  * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26  * SUCH DAMAGE.
27  */
28 
29 #include <errno.h>
30 #include <inttypes.h>
31 #include <signal.h>
32 #include <stdint.h>
33 #include <stdlib.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <unistd.h>
37 
38 #include <mutex>
39 #include <string>
40 #include <unordered_map>
41 #include <utility>
42 #include <vector>
43 
44 #include <android-base/stringprintf.h>
45 #include <android-base/thread_annotations.h>
46 #include <platform/bionic/macros.h>
47 
48 #include "Config.h"
49 #include "DebugData.h"
50 #include "PointerData.h"
51 #include "backtrace.h"
52 #include "debug_log.h"
53 #include "malloc_debug.h"
54 #include "UnwindBacktrace.h"
55 
56 extern "C" char* __cxa_demangle(const char*, char*, size_t*, int*);
57 
58 std::atomic_uint8_t PointerData::backtrace_enabled_;
59 std::atomic_bool PointerData::backtrace_dump_;
60 
61 std::mutex PointerData::pointer_mutex_;
62 std::unordered_map<uintptr_t, PointerInfoType> PointerData::pointers_ GUARDED_BY(
63     PointerData::pointer_mutex_);
64 
65 std::mutex PointerData::frame_mutex_;
66 std::unordered_map<FrameKeyType, size_t> PointerData::key_to_index_ GUARDED_BY(
67     PointerData::frame_mutex_);
68 std::unordered_map<size_t, FrameInfoType> PointerData::frames_ GUARDED_BY(PointerData::frame_mutex_);
69 std::unordered_map<size_t, std::vector<unwindstack::FrameData>> PointerData::backtraces_info_
70     GUARDED_BY(PointerData::frame_mutex_);
71 constexpr size_t kBacktraceEmptyIndex = 1;
72 size_t PointerData::cur_hash_index_ GUARDED_BY(PointerData::frame_mutex_);
73 
74 std::mutex PointerData::free_pointer_mutex_;
75 std::deque<FreePointerInfoType> PointerData::free_pointers_ GUARDED_BY(
76     PointerData::free_pointer_mutex_);
77 
78 // Buffer to use for comparison.
79 static constexpr size_t kCompareBufferSize = 512 * 1024;
80 static std::vector<uint8_t> g_cmp_mem(0);
81 
ToggleBacktraceEnable(int,siginfo_t *,void *)82 static void ToggleBacktraceEnable(int, siginfo_t*, void*) {
83   g_debug->pointer->ToggleBacktraceEnabled();
84 }
85 
EnableDump(int,siginfo_t *,void *)86 static void EnableDump(int, siginfo_t*, void*) {
87   g_debug->pointer->EnableDumping();
88 }
89 
PointerData(DebugData * debug_data)90 PointerData::PointerData(DebugData* debug_data) : OptionData(debug_data) {}
91 
Initialize(const Config & config)92 bool PointerData::Initialize(const Config& config) NO_THREAD_SAFETY_ANALYSIS {
93   pointers_.clear();
94   key_to_index_.clear();
95   frames_.clear();
96   free_pointers_.clear();
97   // A hash index of kBacktraceEmptyIndex indicates that we tried to get
98   // a backtrace, but there was nothing recorded.
99   cur_hash_index_ = kBacktraceEmptyIndex + 1;
100 
101   backtrace_enabled_ = config.backtrace_enabled();
102   if (config.backtrace_enable_on_signal()) {
103     struct sigaction64 enable_act = {};
104     enable_act.sa_sigaction = ToggleBacktraceEnable;
105     enable_act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
106     if (sigaction64(config.backtrace_signal(), &enable_act, nullptr) != 0) {
107       error_log("Unable to set up backtrace signal enable function: %s", strerror(errno));
108       return false;
109     }
110     if (config.options() & VERBOSE) {
111       info_log("%s: Run: 'kill -%d %d' to enable backtracing.", getprogname(),
112                config.backtrace_signal(), getpid());
113     }
114   }
115 
116   if (config.options() & BACKTRACE) {
117     struct sigaction64 act = {};
118     act.sa_sigaction = EnableDump;
119     act.sa_flags = SA_RESTART | SA_SIGINFO | SA_ONSTACK;
120     if (sigaction64(config.backtrace_dump_signal(), &act, nullptr) != 0) {
121       error_log("Unable to set up backtrace dump signal function: %s", strerror(errno));
122       return false;
123     }
124     if (config.options() & VERBOSE) {
125       info_log("%s: Run: 'kill -%d %d' to dump the backtrace.", getprogname(),
126                config.backtrace_dump_signal(), getpid());
127     }
128   }
129 
130   backtrace_dump_ = false;
131 
132   if (config.options() & FREE_TRACK) {
133     g_cmp_mem.resize(kCompareBufferSize, config.fill_free_value());
134   }
135   return true;
136 }
137 
AddBacktrace(size_t num_frames)138 size_t PointerData::AddBacktrace(size_t num_frames) {
139   std::vector<uintptr_t> frames;
140   std::vector<unwindstack::FrameData> frames_info;
141   if (g_debug->config().options() & BACKTRACE_FULL) {
142     if (!Unwind(&frames, &frames_info, num_frames)) {
143       return kBacktraceEmptyIndex;
144     }
145   } else {
146     frames.resize(num_frames);
147     num_frames = backtrace_get(frames.data(), frames.size());
148     if (num_frames == 0) {
149       return kBacktraceEmptyIndex;
150     }
151   }
152 
153   FrameKeyType key{.num_frames = num_frames, .frames = frames.data()};
154   size_t hash_index;
155   std::lock_guard<std::mutex> frame_guard(frame_mutex_);
156   auto entry = key_to_index_.find(key);
157   if (entry == key_to_index_.end()) {
158     frames.resize(num_frames);
159     hash_index = cur_hash_index_++;
160     key.frames = frames.data();
161     key_to_index_.emplace(key, hash_index);
162 
163     frames_.emplace(hash_index, FrameInfoType{.references = 1, .frames = std::move(frames)});
164     if (g_debug->config().options() & BACKTRACE_FULL) {
165       backtraces_info_.emplace(hash_index, std::move(frames_info));
166     }
167   } else {
168     hash_index = entry->second;
169     FrameInfoType* frame_info = &frames_[hash_index];
170     frame_info->references++;
171   }
172   return hash_index;
173 }
174 
RemoveBacktrace(size_t hash_index)175 void PointerData::RemoveBacktrace(size_t hash_index) {
176   if (hash_index <= kBacktraceEmptyIndex) {
177     return;
178   }
179 
180   std::lock_guard<std::mutex> frame_guard(frame_mutex_);
181   auto frame_entry = frames_.find(hash_index);
182   if (frame_entry == frames_.end()) {
183     error_log("hash_index %zu does not have matching frame data.", hash_index);
184     return;
185   }
186   FrameInfoType* frame_info = &frame_entry->second;
187   if (--frame_info->references == 0) {
188     FrameKeyType key{.num_frames = frame_info->frames.size(), .frames = frame_info->frames.data()};
189     key_to_index_.erase(key);
190     frames_.erase(hash_index);
191     if (g_debug->config().options() & BACKTRACE_FULL) {
192       backtraces_info_.erase(hash_index);
193     }
194   }
195 }
196 
Add(const void * ptr,size_t pointer_size)197 void PointerData::Add(const void* ptr, size_t pointer_size) {
198   uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
199   size_t hash_index = 0;
200   if (backtrace_enabled_) {
201     hash_index = AddBacktrace(g_debug->config().backtrace_frames());
202   }
203 
204   std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
205   pointers_[pointer] = PointerInfoType{PointerInfoType::GetEncodedSize(pointer_size), hash_index};
206 }
207 
Remove(const void * ptr)208 void PointerData::Remove(const void* ptr) {
209   uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
210   size_t hash_index;
211   {
212     std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
213     auto entry = pointers_.find(pointer);
214     if (entry == pointers_.end()) {
215       // Attempt to remove unknown pointer.
216       error_log("No tracked pointer found for 0x%" PRIxPTR, pointer);
217       return;
218     }
219     hash_index = entry->second.hash_index;
220     pointers_.erase(pointer);
221   }
222 
223   RemoveBacktrace(hash_index);
224 }
225 
GetFrames(const void * ptr,uintptr_t * frames,size_t max_frames)226 size_t PointerData::GetFrames(const void* ptr, uintptr_t* frames, size_t max_frames) {
227   uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
228   size_t hash_index;
229   {
230     std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
231     auto entry = pointers_.find(pointer);
232     if (entry == pointers_.end()) {
233       return 0;
234     }
235     hash_index = entry->second.hash_index;
236   }
237 
238   if (hash_index <= kBacktraceEmptyIndex) {
239     return 0;
240   }
241 
242   std::lock_guard<std::mutex> frame_guard(frame_mutex_);
243   auto frame_entry = frames_.find(hash_index);
244   if (frame_entry == frames_.end()) {
245     return 0;
246   }
247   FrameInfoType* frame_info = &frame_entry->second;
248   if (max_frames > frame_info->frames.size()) {
249     max_frames = frame_info->frames.size();
250   }
251   memcpy(frames, &frame_info->frames[0], max_frames * sizeof(uintptr_t));
252 
253   return max_frames;
254 }
255 
LogBacktrace(size_t hash_index)256 void PointerData::LogBacktrace(size_t hash_index) {
257   std::lock_guard<std::mutex> frame_guard(frame_mutex_);
258   if (g_debug->config().options() & BACKTRACE_FULL) {
259     auto backtrace_info_entry = backtraces_info_.find(hash_index);
260     if (backtrace_info_entry != backtraces_info_.end()) {
261       UnwindLog(backtrace_info_entry->second);
262       return;
263     }
264   } else {
265     auto frame_entry = frames_.find(hash_index);
266     if (frame_entry != frames_.end()) {
267       FrameInfoType* frame_info = &frame_entry->second;
268       backtrace_log(frame_info->frames.data(), frame_info->frames.size());
269       return;
270     }
271   }
272   error_log("  hash_index %zu does not have matching frame data.", hash_index);
273 }
274 
LogFreeError(const FreePointerInfoType & info,size_t max_cmp_bytes)275 void PointerData::LogFreeError(const FreePointerInfoType& info, size_t max_cmp_bytes) {
276   error_log(LOG_DIVIDER);
277   uint8_t* memory = reinterpret_cast<uint8_t*>(info.pointer);
278   error_log("+++ ALLOCATION %p USED AFTER FREE", memory);
279   uint8_t fill_free_value = g_debug->config().fill_free_value();
280   for (size_t i = 0; i < max_cmp_bytes; i++) {
281     if (memory[i] != fill_free_value) {
282       error_log("  allocation[%zu] = 0x%02x (expected 0x%02x)", i, memory[i], fill_free_value);
283     }
284   }
285 
286   if (info.hash_index > kBacktraceEmptyIndex) {
287     error_log("Backtrace at time of free:");
288     LogBacktrace(info.hash_index);
289   }
290 
291   error_log(LOG_DIVIDER);
292   if (g_debug->config().options() & ABORT_ON_ERROR) {
293     abort();
294   }
295 }
296 
VerifyFreedPointer(const FreePointerInfoType & info)297 void PointerData::VerifyFreedPointer(const FreePointerInfoType& info) {
298   size_t usable_size;
299   if (g_debug->HeaderEnabled()) {
300     // Check to see if the tag data has been damaged.
301     Header* header = g_debug->GetHeader(reinterpret_cast<const void*>(info.pointer));
302     if (header->tag != DEBUG_FREE_TAG) {
303       error_log(LOG_DIVIDER);
304       error_log("+++ ALLOCATION 0x%" PRIxPTR " HAS CORRUPTED HEADER TAG 0x%x AFTER FREE",
305                 info.pointer, header->tag);
306       error_log(LOG_DIVIDER);
307       if (g_debug->config().options() & ABORT_ON_ERROR) {
308         abort();
309       }
310 
311       // Stop processing here, it is impossible to tell how the header
312       // may have been damaged.
313       return;
314     }
315     usable_size = header->usable_size;
316   } else {
317     usable_size = g_dispatch->malloc_usable_size(reinterpret_cast<const void*>(info.pointer));
318   }
319 
320   size_t bytes = (usable_size < g_debug->config().fill_on_free_bytes())
321                      ? usable_size
322                      : g_debug->config().fill_on_free_bytes();
323   size_t max_cmp_bytes = bytes;
324   const uint8_t* memory = reinterpret_cast<const uint8_t*>(info.pointer);
325   while (bytes > 0) {
326     size_t bytes_to_cmp = (bytes < g_cmp_mem.size()) ? bytes : g_cmp_mem.size();
327     if (memcmp(memory, g_cmp_mem.data(), bytes_to_cmp) != 0) {
328       LogFreeError(info, max_cmp_bytes);
329     }
330     bytes -= bytes_to_cmp;
331     memory = &memory[bytes_to_cmp];
332   }
333 }
334 
AddFreed(const void * ptr)335 void* PointerData::AddFreed(const void* ptr) {
336   uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
337 
338   size_t hash_index = 0;
339   size_t num_frames = g_debug->config().free_track_backtrace_num_frames();
340   if (num_frames) {
341     hash_index = AddBacktrace(num_frames);
342   }
343 
344   void* last = nullptr;
345   std::lock_guard<std::mutex> freed_guard(free_pointer_mutex_);
346   if (free_pointers_.size() == g_debug->config().free_track_allocations()) {
347     FreePointerInfoType info(free_pointers_.front());
348     free_pointers_.pop_front();
349     VerifyFreedPointer(info);
350     RemoveBacktrace(info.hash_index);
351     last = reinterpret_cast<void*>(info.pointer);
352   }
353 
354   free_pointers_.emplace_back(FreePointerInfoType{pointer, hash_index});
355   return last;
356 }
357 
LogFreeBacktrace(const void * ptr)358 void PointerData::LogFreeBacktrace(const void* ptr) {
359   size_t hash_index = 0;
360   {
361     uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
362     std::lock_guard<std::mutex> freed_guard(free_pointer_mutex_);
363     for (const auto& info : free_pointers_) {
364       if (info.pointer == pointer) {
365         hash_index = info.hash_index;
366         break;
367       }
368     }
369   }
370 
371   if (hash_index <= kBacktraceEmptyIndex) {
372     return;
373   }
374 
375   error_log("Backtrace of original free:");
376   LogBacktrace(hash_index);
377 }
378 
VerifyAllFreed()379 void PointerData::VerifyAllFreed() {
380   std::lock_guard<std::mutex> freed_guard(free_pointer_mutex_);
381   for (auto& free_info : free_pointers_) {
382     VerifyFreedPointer(free_info);
383   }
384 }
385 
GetList(std::vector<ListInfoType> * list,bool only_with_backtrace)386 void PointerData::GetList(std::vector<ListInfoType>* list, bool only_with_backtrace)
387     REQUIRES(pointer_mutex_, frame_mutex_) {
388   for (const auto& entry : pointers_) {
389     FrameInfoType* frame_info = nullptr;
390     std::vector<unwindstack::FrameData>* backtrace_info = nullptr;
391     size_t hash_index = entry.second.hash_index;
392     if (hash_index > kBacktraceEmptyIndex) {
393       auto frame_entry = frames_.find(hash_index);
394       if (frame_entry == frames_.end()) {
395         // Somehow wound up with a pointer with a valid hash_index, but
396         // no frame data. This should not be possible since adding a pointer
397         // occurs after the hash_index and frame data have been added.
398         // When removing a pointer, the pointer is deleted before the frame
399         // data.
400         error_log("Pointer 0x%" PRIxPTR " hash_index %zu does not exist.", entry.first, hash_index);
401       } else {
402         frame_info = &frame_entry->second;
403       }
404 
405       if (g_debug->config().options() & BACKTRACE_FULL) {
406         auto backtrace_entry = backtraces_info_.find(hash_index);
407         if (backtrace_entry == backtraces_info_.end()) {
408           error_log("Pointer 0x%" PRIxPTR " hash_index %zu does not exist.", entry.first, hash_index);
409         } else {
410           backtrace_info = &backtrace_entry->second;
411         }
412       }
413     }
414     if (hash_index == 0 && only_with_backtrace) {
415       continue;
416     }
417 
418     list->emplace_back(ListInfoType{entry.first, 1, entry.second.RealSize(),
419                                     entry.second.ZygoteChildAlloc(), frame_info, backtrace_info});
420   }
421 
422   // Sort by the size of the allocation.
423   std::sort(list->begin(), list->end(), [](const ListInfoType& a, const ListInfoType& b) {
424     // Put zygote child allocations first.
425     bool a_zygote_child_alloc = a.zygote_child_alloc;
426     bool b_zygote_child_alloc = b.zygote_child_alloc;
427     if (a_zygote_child_alloc && !b_zygote_child_alloc) {
428       return false;
429     }
430     if (!a_zygote_child_alloc && b_zygote_child_alloc) {
431       return true;
432     }
433 
434     // Sort by size, descending order.
435     if (a.size != b.size) return a.size > b.size;
436 
437     // Put pointers with no backtrace last.
438     FrameInfoType* a_frame = a.frame_info;
439     FrameInfoType* b_frame = b.frame_info;
440     if (a_frame == nullptr && b_frame != nullptr) {
441       return false;
442     } else if (a_frame != nullptr && b_frame == nullptr) {
443       return true;
444     } else if (a_frame == nullptr && b_frame == nullptr) {
445       return a.pointer < b.pointer;
446     }
447 
448     // Put the pointers with longest backtrace first.
449     if (a_frame->frames.size() != b_frame->frames.size()) {
450       return a_frame->frames.size() > b_frame->frames.size();
451     }
452 
453     // Last sort by pointer.
454     return a.pointer < b.pointer;
455   });
456 }
457 
GetUniqueList(std::vector<ListInfoType> * list,bool only_with_backtrace)458 void PointerData::GetUniqueList(std::vector<ListInfoType>* list, bool only_with_backtrace)
459     REQUIRES(pointer_mutex_, frame_mutex_) {
460   GetList(list, only_with_backtrace);
461 
462   // Remove duplicates of size/backtraces.
463   for (auto iter = list->begin(); iter != list->end();) {
464     auto dup_iter = iter + 1;
465     bool zygote_child_alloc = iter->zygote_child_alloc;
466     size_t size = iter->size;
467     FrameInfoType* frame_info = iter->frame_info;
468     for (; dup_iter != list->end(); ++dup_iter) {
469       if (zygote_child_alloc != dup_iter->zygote_child_alloc || size != dup_iter->size ||
470           frame_info != dup_iter->frame_info) {
471         break;
472       }
473       iter->num_allocations++;
474     }
475     iter = list->erase(iter + 1, dup_iter);
476   }
477 }
478 
LogLeaks()479 void PointerData::LogLeaks() {
480   std::vector<ListInfoType> list;
481 
482   std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
483   std::lock_guard<std::mutex> frame_guard(frame_mutex_);
484   GetList(&list, false);
485 
486   size_t track_count = 0;
487   for (const auto& list_info : list) {
488     error_log("+++ %s leaked block of size %zu at 0x%" PRIxPTR " (leak %zu of %zu)", getprogname(),
489               list_info.size, list_info.pointer, ++track_count, list.size());
490     if (list_info.backtrace_info != nullptr) {
491       error_log("Backtrace at time of allocation:");
492       UnwindLog(*list_info.backtrace_info);
493     } else if (list_info.frame_info != nullptr) {
494       error_log("Backtrace at time of allocation:");
495       backtrace_log(list_info.frame_info->frames.data(), list_info.frame_info->frames.size());
496     }
497     // Do not bother to free the pointers, we are about to exit any way.
498   }
499 }
500 
GetAllocList(std::vector<ListInfoType> * list)501 void PointerData::GetAllocList(std::vector<ListInfoType>* list) {
502   std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
503   std::lock_guard<std::mutex> frame_guard(frame_mutex_);
504 
505   if (pointers_.empty()) {
506     return;
507   }
508 
509   GetList(list, false);
510 }
511 
GetInfo(uint8_t ** info,size_t * overall_size,size_t * info_size,size_t * total_memory,size_t * backtrace_size)512 void PointerData::GetInfo(uint8_t** info, size_t* overall_size, size_t* info_size,
513                           size_t* total_memory, size_t* backtrace_size) {
514   std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
515   std::lock_guard<std::mutex> frame_guard(frame_mutex_);
516 
517   if (pointers_.empty()) {
518     return;
519   }
520 
521   std::vector<ListInfoType> list;
522   GetUniqueList(&list, true);
523   if (list.empty()) {
524     return;
525   }
526 
527   *backtrace_size = g_debug->config().backtrace_frames();
528   *info_size = sizeof(size_t) * 2 + sizeof(uintptr_t) * *backtrace_size;
529   *overall_size = *info_size * list.size();
530   *info = reinterpret_cast<uint8_t*>(g_dispatch->calloc(*info_size, list.size()));
531   if (*info == nullptr) {
532     return;
533   }
534 
535   uint8_t* data = *info;
536   *total_memory = 0;
537   for (const auto& list_info : list) {
538     FrameInfoType* frame_info = list_info.frame_info;
539     *total_memory += list_info.size * list_info.num_allocations;
540     size_t allocation_size =
541         PointerInfoType::GetEncodedSize(list_info.zygote_child_alloc, list_info.size);
542     memcpy(data, &allocation_size, sizeof(size_t));
543     memcpy(&data[sizeof(size_t)], &list_info.num_allocations, sizeof(size_t));
544     if (frame_info != nullptr) {
545       memcpy(&data[2 * sizeof(size_t)], frame_info->frames.data(),
546              frame_info->frames.size() * sizeof(uintptr_t));
547     }
548     data += *info_size;
549   }
550 }
551 
Exists(const void * ptr)552 bool PointerData::Exists(const void* ptr) {
553   uintptr_t pointer = reinterpret_cast<uintptr_t>(ptr);
554   std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
555   return pointers_.count(pointer) != 0;
556 }
557 
DumpLiveToFile(int fd)558 void PointerData::DumpLiveToFile(int fd) {
559   std::vector<ListInfoType> list;
560 
561   std::lock_guard<std::mutex> pointer_guard(pointer_mutex_);
562   std::lock_guard<std::mutex> frame_guard(frame_mutex_);
563   GetUniqueList(&list, false);
564 
565   size_t total_memory = 0;
566   for (const auto& info : list) {
567     total_memory += info.size * info.num_allocations;
568   }
569 
570   dprintf(fd, "Total memory: %zu\n", total_memory);
571   dprintf(fd, "Allocation records: %zd\n", list.size());
572   dprintf(fd, "Backtrace size: %zu\n", g_debug->config().backtrace_frames());
573   dprintf(fd, "\n");
574 
575   for (const auto& info : list) {
576     dprintf(fd, "z %d  sz %8zu  num    %zu  bt", (info.zygote_child_alloc) ? 1 : 0, info.size,
577             info.num_allocations);
578     FrameInfoType* frame_info = info.frame_info;
579     if (frame_info != nullptr) {
580       for (size_t i = 0; i < frame_info->frames.size(); i++) {
581         if (frame_info->frames[i] == 0) {
582           break;
583         }
584         dprintf(fd, " %" PRIxPTR, frame_info->frames[i]);
585       }
586     }
587     dprintf(fd, "\n");
588     if (info.backtrace_info != nullptr) {
589       dprintf(fd, "  bt_info");
590       for (const auto& frame : *info.backtrace_info) {
591         dprintf(fd, " {");
592         if (frame.map_info != nullptr && !frame.map_info->name().empty()) {
593           dprintf(fd, "\"%s\"", frame.map_info->name().c_str());
594         } else {
595           dprintf(fd, "\"\"");
596         }
597         dprintf(fd, " %" PRIx64, frame.rel_pc);
598         if (frame.function_name.empty()) {
599           dprintf(fd, " \"\" 0}");
600         } else {
601           char* demangled_name = __cxa_demangle(frame.function_name.c_str(), nullptr, nullptr,
602                                                 nullptr);
603           const char* name;
604           if (demangled_name != nullptr) {
605             name = demangled_name;
606           } else {
607             name = frame.function_name.c_str();
608           }
609           dprintf(fd, " \"%s\" %" PRIx64 "}", name, frame.function_offset);
610           free(demangled_name);
611         }
612       }
613       dprintf(fd, "\n");
614     }
615   }
616 }
617 
PrepareFork()618 void PointerData::PrepareFork() NO_THREAD_SAFETY_ANALYSIS {
619   free_pointer_mutex_.lock();
620   pointer_mutex_.lock();
621   frame_mutex_.lock();
622 }
623 
PostForkParent()624 void PointerData::PostForkParent() NO_THREAD_SAFETY_ANALYSIS {
625   frame_mutex_.unlock();
626   pointer_mutex_.unlock();
627   free_pointer_mutex_.unlock();
628 }
629 
PostForkChild()630 void PointerData::PostForkChild() __attribute__((no_thread_safety_analysis)) {
631   // Make sure that any potential mutexes have been released and are back
632   // to an initial state.
633   frame_mutex_.try_lock();
634   frame_mutex_.unlock();
635   pointer_mutex_.try_lock();
636   pointer_mutex_.unlock();
637   free_pointer_mutex_.try_lock();
638   free_pointer_mutex_.unlock();
639 }
640