125 lines · cpp
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-exception6//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 "asan/asan_allocator.h"15#include "lsan/lsan_common.h"16#include "sanitizer_common/sanitizer_common.h"17#include "sanitizer_common/sanitizer_stackdepot.h"18#include "sanitizer_common/sanitizer_stacktrace.h"19 20#if CAN_SANITIZE_LEAKS21 22namespace __asan {23 24struct AllocationSite {25 u32 id;26 uptr total_size;27 uptr count;28};29 30class HeapProfile {31 public:32 HeapProfile() { allocations_.reserve(1024); }33 34 void ProcessChunk(const AsanChunkView &cv) {35 if (cv.IsAllocated()) {36 total_allocated_user_size_ += cv.UsedSize();37 total_allocated_count_++;38 u32 id = cv.GetAllocStackId();39 if (id)40 Insert(id, cv.UsedSize());41 } else if (cv.IsQuarantined()) {42 total_quarantined_user_size_ += cv.UsedSize();43 total_quarantined_count_++;44 } else {45 total_other_count_++;46 }47 }48 49 void Print(uptr top_percent, uptr max_number_of_contexts) {50 Sort(allocations_.data(), allocations_.size(),51 [](const AllocationSite &a, const AllocationSite &b) {52 return a.total_size > b.total_size;53 });54 CHECK(total_allocated_user_size_);55 uptr total_shown = 0;56 Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: "57 "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; "58 "showing top %zd%% (at most %zd unique contexts)\n",59 total_allocated_user_size_, total_allocated_count_,60 total_quarantined_user_size_, total_quarantined_count_,61 total_other_count_, total_allocated_count_ +62 total_quarantined_count_ + total_other_count_, top_percent,63 max_number_of_contexts);64 for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts);65 i++) {66 auto &a = allocations_[i];67 Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size,68 a.total_size * 100 / total_allocated_user_size_, a.count);69 StackDepotGet(a.id).Print();70 total_shown += a.total_size;71 if (total_shown * 100 / total_allocated_user_size_ > top_percent)72 break;73 }74 }75 76 private:77 uptr total_allocated_user_size_ = 0;78 uptr total_allocated_count_ = 0;79 uptr total_quarantined_user_size_ = 0;80 uptr total_quarantined_count_ = 0;81 uptr total_other_count_ = 0;82 InternalMmapVector<AllocationSite> allocations_;83 84 void Insert(u32 id, uptr size) {85 // Linear lookup will be good enough for most cases (although not all).86 for (uptr i = 0; i < allocations_.size(); i++) {87 if (allocations_[i].id == id) {88 allocations_[i].total_size += size;89 allocations_[i].count++;90 return;91 }92 }93 allocations_.push_back({id, size, 1});94 }95};96 97static void ChunkCallback(uptr chunk, void *arg) {98 reinterpret_cast<HeapProfile*>(arg)->ProcessChunk(99 FindHeapChunkByAllocBeg(chunk));100}101 102static void MemoryProfileCB(uptr top_percent, uptr max_number_of_contexts) {103 HeapProfile hp;104 __lsan::LockAllocator();105 __lsan::ForEachChunk(ChunkCallback, &hp);106 __lsan::UnlockAllocator();107 hp.Print(top_percent, max_number_of_contexts);108 109 if (Verbosity())110 __asan_print_accumulated_stats();111}112} // namespace __asan113 114#endif // CAN_SANITIZE_LEAKS115 116extern "C" {117SANITIZER_INTERFACE_ATTRIBUTE118void __sanitizer_print_memory_profile(uptr top_percent,119 uptr max_number_of_contexts) {120#if CAN_SANITIZE_LEAKS121 __asan::MemoryProfileCB(top_percent, max_number_of_contexts);122#endif // CAN_SANITIZE_LEAKS123}124} // extern "C"125