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