1//===-- asan_memory_profile.cc.cc -----------------------------------------===// 2// 3// The LLVM Compiler Infrastructure 4// 5// This file is distributed under the University of Illinois Open Source 6// License. See LICENSE.TXT for details. 7// 8//===----------------------------------------------------------------------===// 9// 10// This file is a part of AddressSanitizer, an address sanity checker. 11// 12// This file implements __sanitizer_print_memory_profile. 13//===----------------------------------------------------------------------===// 14 15#include "sanitizer_common/sanitizer_common.h" 16#include "sanitizer_common/sanitizer_stackdepot.h" 17#include "sanitizer_common/sanitizer_stacktrace.h" 18#include "sanitizer_common/sanitizer_stoptheworld.h" 19#include "lsan/lsan_common.h" 20#include "asan/asan_allocator.h" 21 22#if CAN_SANITIZE_LEAKS 23 24namespace __asan { 25 26struct AllocationSite { 27 u32 id; 28 uptr total_size; 29 uptr count; 30}; 31 32class HeapProfile { 33 public: 34 HeapProfile() { allocations_.reserve(1024); } 35 36 void ProcessChunk(const AsanChunkView &cv) { 37 if (cv.IsAllocated()) { 38 total_allocated_user_size_ += cv.UsedSize(); 39 total_allocated_count_++; 40 u32 id = cv.GetAllocStackId(); 41 if (id) 42 Insert(id, cv.UsedSize()); 43 } else if (cv.IsQuarantined()) { 44 total_quarantined_user_size_ += cv.UsedSize(); 45 total_quarantined_count_++; 46 } else { 47 total_other_count_++; 48 } 49 } 50 51 void Print(uptr top_percent, uptr max_number_of_contexts) { 52 Sort(allocations_.data(), allocations_.size(), 53 [](const AllocationSite &a, const AllocationSite &b) { 54 return a.total_size > b.total_size; 55 }); 56 CHECK(total_allocated_user_size_); 57 uptr total_shown = 0; 58 Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: " 59 "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; " 60 "showing top %zd%% (at most %zd unique contexts)\n", 61 total_allocated_user_size_, total_allocated_count_, 62 total_quarantined_user_size_, total_quarantined_count_, 63 total_other_count_, total_allocated_count_ + 64 total_quarantined_count_ + total_other_count_, top_percent, 65 max_number_of_contexts); 66 for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts); 67 i++) { 68 auto &a = allocations_[i]; 69 Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size, 70 a.total_size * 100 / total_allocated_user_size_, a.count); 71 StackDepotGet(a.id).Print(); 72 total_shown += a.total_size; 73 if (total_shown * 100 / total_allocated_user_size_ > top_percent) 74 break; 75 } 76 } 77 78 private: 79 uptr total_allocated_user_size_ = 0; 80 uptr total_allocated_count_ = 0; 81 uptr total_quarantined_user_size_ = 0; 82 uptr total_quarantined_count_ = 0; 83 uptr total_other_count_ = 0; 84 InternalMmapVector<AllocationSite> allocations_; 85 86 void Insert(u32 id, uptr size) { 87 // Linear lookup will be good enough for most cases (although not all). 88 for (uptr i = 0; i < allocations_.size(); i++) { 89 if (allocations_[i].id == id) { 90 allocations_[i].total_size += size; 91 allocations_[i].count++; 92 return; 93 } 94 } 95 allocations_.push_back({id, size, 1}); 96 } 97}; 98 99static void ChunkCallback(uptr chunk, void *arg) { 100 reinterpret_cast<HeapProfile*>(arg)->ProcessChunk( 101 FindHeapChunkByAllocBeg(chunk)); 102} 103 104static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list, 105 void *argument) { 106 HeapProfile hp; 107 __lsan::ForEachChunk(ChunkCallback, &hp); 108 uptr *Arg = reinterpret_cast<uptr*>(argument); 109 hp.Print(Arg[0], Arg[1]); 110 111 if (Verbosity()) 112 __asan_print_accumulated_stats(); 113} 114 115} // namespace __asan 116 117#endif // CAN_SANITIZE_LEAKS 118 119extern "C" { 120SANITIZER_INTERFACE_ATTRIBUTE 121void __sanitizer_print_memory_profile(uptr top_percent, 122 uptr max_number_of_contexts) { 123#if CAN_SANITIZE_LEAKS 124 uptr Arg[2]; 125 Arg[0] = top_percent; 126 Arg[1] = max_number_of_contexts; 127 __sanitizer::StopTheWorld(__asan::MemoryProfileCB, Arg); 128#endif // CAN_SANITIZE_LEAKS 129} 130} // extern "C" 131