xref: /freebsd-src/contrib/llvm-project/compiler-rt/lib/asan/asan_memory_profile.cpp (revision 06c3fb2749bda94cb5201f81ffdb8fa6c3161b2e)
168d75effSDimitry Andric //===-- asan_memory_profile.cpp ----------------------------------------===//
268d75effSDimitry Andric //
368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
668d75effSDimitry Andric //
768d75effSDimitry Andric //===----------------------------------------------------------------------===//
868d75effSDimitry Andric //
968d75effSDimitry Andric // This file is a part of AddressSanitizer, an address sanity checker.
1068d75effSDimitry Andric //
1168d75effSDimitry Andric // This file implements __sanitizer_print_memory_profile.
1268d75effSDimitry Andric //===----------------------------------------------------------------------===//
1368d75effSDimitry Andric 
14*06c3fb27SDimitry Andric #include "asan/asan_allocator.h"
15*06c3fb27SDimitry Andric #include "lsan/lsan_common.h"
1668d75effSDimitry Andric #include "sanitizer_common/sanitizer_common.h"
1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
1868d75effSDimitry Andric #include "sanitizer_common/sanitizer_stacktrace.h"
1968d75effSDimitry Andric 
2068d75effSDimitry Andric #if CAN_SANITIZE_LEAKS
2168d75effSDimitry Andric 
2268d75effSDimitry Andric namespace __asan {
2368d75effSDimitry Andric 
2468d75effSDimitry Andric struct AllocationSite {
2568d75effSDimitry Andric   u32 id;
2668d75effSDimitry Andric   uptr total_size;
2768d75effSDimitry Andric   uptr count;
2868d75effSDimitry Andric };
2968d75effSDimitry Andric 
3068d75effSDimitry Andric class HeapProfile {
3168d75effSDimitry Andric  public:
HeapProfile()3268d75effSDimitry Andric   HeapProfile() { allocations_.reserve(1024); }
3368d75effSDimitry Andric 
ProcessChunk(const AsanChunkView & cv)3468d75effSDimitry Andric   void ProcessChunk(const AsanChunkView &cv) {
3568d75effSDimitry Andric     if (cv.IsAllocated()) {
3668d75effSDimitry Andric       total_allocated_user_size_ += cv.UsedSize();
3768d75effSDimitry Andric       total_allocated_count_++;
3868d75effSDimitry Andric       u32 id = cv.GetAllocStackId();
3968d75effSDimitry Andric       if (id)
4068d75effSDimitry Andric         Insert(id, cv.UsedSize());
4168d75effSDimitry Andric     } else if (cv.IsQuarantined()) {
4268d75effSDimitry Andric       total_quarantined_user_size_ += cv.UsedSize();
4368d75effSDimitry Andric       total_quarantined_count_++;
4468d75effSDimitry Andric     } else {
4568d75effSDimitry Andric       total_other_count_++;
4668d75effSDimitry Andric     }
4768d75effSDimitry Andric   }
4868d75effSDimitry Andric 
Print(uptr top_percent,uptr max_number_of_contexts)4968d75effSDimitry Andric   void Print(uptr top_percent, uptr max_number_of_contexts) {
5068d75effSDimitry Andric     Sort(allocations_.data(), allocations_.size(),
5168d75effSDimitry Andric          [](const AllocationSite &a, const AllocationSite &b) {
5268d75effSDimitry Andric            return a.total_size > b.total_size;
5368d75effSDimitry Andric          });
5468d75effSDimitry Andric     CHECK(total_allocated_user_size_);
5568d75effSDimitry Andric     uptr total_shown = 0;
5668d75effSDimitry Andric     Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: "
5768d75effSDimitry Andric            "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; "
5868d75effSDimitry Andric            "showing top %zd%% (at most %zd unique contexts)\n",
5968d75effSDimitry Andric            total_allocated_user_size_, total_allocated_count_,
6068d75effSDimitry Andric            total_quarantined_user_size_, total_quarantined_count_,
6168d75effSDimitry Andric            total_other_count_, total_allocated_count_ +
6268d75effSDimitry Andric            total_quarantined_count_ + total_other_count_, top_percent,
6368d75effSDimitry Andric            max_number_of_contexts);
6468d75effSDimitry Andric     for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts);
6568d75effSDimitry Andric          i++) {
6668d75effSDimitry Andric       auto &a = allocations_[i];
6768d75effSDimitry Andric       Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size,
6868d75effSDimitry Andric              a.total_size * 100 / total_allocated_user_size_, a.count);
6968d75effSDimitry Andric       StackDepotGet(a.id).Print();
7068d75effSDimitry Andric       total_shown += a.total_size;
7168d75effSDimitry Andric       if (total_shown * 100 / total_allocated_user_size_ > top_percent)
7268d75effSDimitry Andric         break;
7368d75effSDimitry Andric     }
7468d75effSDimitry Andric   }
7568d75effSDimitry Andric 
7668d75effSDimitry Andric  private:
7768d75effSDimitry Andric   uptr total_allocated_user_size_ = 0;
7868d75effSDimitry Andric   uptr total_allocated_count_ = 0;
7968d75effSDimitry Andric   uptr total_quarantined_user_size_ = 0;
8068d75effSDimitry Andric   uptr total_quarantined_count_ = 0;
8168d75effSDimitry Andric   uptr total_other_count_ = 0;
8268d75effSDimitry Andric   InternalMmapVector<AllocationSite> allocations_;
8368d75effSDimitry Andric 
Insert(u32 id,uptr size)8468d75effSDimitry Andric   void Insert(u32 id, uptr size) {
8568d75effSDimitry Andric     // Linear lookup will be good enough for most cases (although not all).
8668d75effSDimitry Andric     for (uptr i = 0; i < allocations_.size(); i++) {
8768d75effSDimitry Andric       if (allocations_[i].id == id) {
8868d75effSDimitry Andric         allocations_[i].total_size += size;
8968d75effSDimitry Andric         allocations_[i].count++;
9068d75effSDimitry Andric         return;
9168d75effSDimitry Andric       }
9268d75effSDimitry Andric     }
9368d75effSDimitry Andric     allocations_.push_back({id, size, 1});
9468d75effSDimitry Andric   }
9568d75effSDimitry Andric };
9668d75effSDimitry Andric 
ChunkCallback(uptr chunk,void * arg)9768d75effSDimitry Andric static void ChunkCallback(uptr chunk, void *arg) {
9868d75effSDimitry Andric   reinterpret_cast<HeapProfile*>(arg)->ProcessChunk(
9968d75effSDimitry Andric       FindHeapChunkByAllocBeg(chunk));
10068d75effSDimitry Andric }
10168d75effSDimitry Andric 
MemoryProfileCB(uptr top_percent,uptr max_number_of_contexts)102*06c3fb27SDimitry Andric static void MemoryProfileCB(uptr top_percent, uptr max_number_of_contexts) {
10368d75effSDimitry Andric   HeapProfile hp;
104*06c3fb27SDimitry Andric   __lsan::LockAllocator();
10568d75effSDimitry Andric   __lsan::ForEachChunk(ChunkCallback, &hp);
106*06c3fb27SDimitry Andric   __lsan::UnlockAllocator();
107*06c3fb27SDimitry Andric   hp.Print(top_percent, max_number_of_contexts);
10868d75effSDimitry Andric 
10968d75effSDimitry Andric   if (Verbosity())
11068d75effSDimitry Andric     __asan_print_accumulated_stats();
11168d75effSDimitry Andric }
11268d75effSDimitry Andric }  // namespace __asan
11368d75effSDimitry Andric 
11468d75effSDimitry Andric #endif  // CAN_SANITIZE_LEAKS
11568d75effSDimitry Andric 
11668d75effSDimitry Andric extern "C" {
11768d75effSDimitry Andric SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_print_memory_profile(uptr top_percent,uptr max_number_of_contexts)11868d75effSDimitry Andric void __sanitizer_print_memory_profile(uptr top_percent,
11968d75effSDimitry Andric                                       uptr max_number_of_contexts) {
12068d75effSDimitry Andric #if CAN_SANITIZE_LEAKS
121*06c3fb27SDimitry Andric   __asan::MemoryProfileCB(top_percent, max_number_of_contexts);
12268d75effSDimitry Andric #endif  // CAN_SANITIZE_LEAKS
12368d75effSDimitry Andric }
12468d75effSDimitry Andric }  // extern "C"
125