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 23namespace __asan { 24 25struct AllocationSite { 26 u32 id; 27 uptr total_size; 28 uptr count; 29}; 30 31class 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 98static void ChunkCallback(uptr chunk, void *arg) { 99 reinterpret_cast<HeapProfile*>(arg)->ProcessChunk( 100 FindHeapChunkByAllocBeg(chunk)); 101} 102 103static 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 118extern "C" { 119SANITIZER_INTERFACE_ATTRIBUTE 120void __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