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 24 namespace __asan { 25 26 struct AllocationSite { 27 u32 id; 28 uptr total_size; 29 uptr count; 30 }; 31 32 class HeapProfile { 33 public: 34 HeapProfile() : allocations_(1024) {} 35 void Insert(u32 id, uptr size) { 36 total_allocated_ += size; 37 total_count_++; 38 // Linear lookup will be good enough for most cases (although not all). 39 for (uptr i = 0; i < allocations_.size(); i++) { 40 if (allocations_[i].id == id) { 41 allocations_[i].total_size += size; 42 allocations_[i].count++; 43 return; 44 } 45 } 46 allocations_.push_back({id, size, 1}); 47 } 48 49 void Print(uptr top_percent) { 50 InternalSort(&allocations_, allocations_.size(), 51 [](const AllocationSite &a, const AllocationSite &b) { 52 return a.total_size > b.total_size; 53 }); 54 CHECK(total_allocated_); 55 uptr total_shown = 0; 56 Printf("Live Heap Allocations: %zd bytes from %zd allocations; " 57 "showing top %zd%%\n", total_allocated_, total_count_, top_percent); 58 for (uptr i = 0; i < allocations_.size(); i++) { 59 auto &a = allocations_[i]; 60 Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size, 61 a.total_size * 100 / total_allocated_, a.count); 62 StackDepotGet(a.id).Print(); 63 total_shown += a.total_size; 64 if (total_shown * 100 / total_allocated_ > top_percent) 65 break; 66 } 67 } 68 69 private: 70 uptr total_allocated_ = 0; 71 uptr total_count_ = 0; 72 InternalMmapVector<AllocationSite> allocations_; 73 }; 74 75 static void ChunkCallback(uptr chunk, void *arg) { 76 HeapProfile *hp = reinterpret_cast<HeapProfile*>(arg); 77 AsanChunkView cv = FindHeapChunkByAddress(chunk); 78 if (!cv.IsAllocated()) return; 79 u32 id = cv.GetAllocStackId(); 80 if (!id) return; 81 hp->Insert(id, cv.UsedSize()); 82 } 83 84 static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list, 85 void *argument) { 86 HeapProfile hp; 87 __lsan::ForEachChunk(ChunkCallback, &hp); 88 hp.Print(reinterpret_cast<uptr>(argument)); 89 } 90 91 } // namespace __asan 92 93 extern "C" { 94 SANITIZER_INTERFACE_ATTRIBUTE 95 void __sanitizer_print_memory_profile(uptr top_percent) { 96 __sanitizer::StopTheWorld(__asan::MemoryProfileCB, (void*)top_percent); 97 } 98 } // extern "C" 99 100 #endif // CAN_SANITIZE_LEAKS 101