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