1ac8e35e1Smrg //===-- asan_memory_profile.cc.cc -----------------------------------------===//
2ac8e35e1Smrg //
3ac8e35e1Smrg // This file is distributed under the University of Illinois Open Source
4ac8e35e1Smrg // License. See LICENSE.TXT for details.
5ac8e35e1Smrg //
6ac8e35e1Smrg //===----------------------------------------------------------------------===//
7ac8e35e1Smrg //
8ac8e35e1Smrg // This file is a part of AddressSanitizer, an address sanity checker.
9ac8e35e1Smrg //
10ac8e35e1Smrg // This file implements __sanitizer_print_memory_profile.
11ac8e35e1Smrg //===----------------------------------------------------------------------===//
12ac8e35e1Smrg 
13ac8e35e1Smrg #include "sanitizer_common/sanitizer_common.h"
14ac8e35e1Smrg #include "sanitizer_common/sanitizer_stackdepot.h"
15ac8e35e1Smrg #include "sanitizer_common/sanitizer_stacktrace.h"
16ac8e35e1Smrg #include "sanitizer_common/sanitizer_stoptheworld.h"
17ac8e35e1Smrg #include "lsan/lsan_common.h"
18ac8e35e1Smrg #include "asan/asan_allocator.h"
19ac8e35e1Smrg 
20ac8e35e1Smrg #if CAN_SANITIZE_LEAKS
21ac8e35e1Smrg 
22ac8e35e1Smrg namespace __asan {
23ac8e35e1Smrg 
24ac8e35e1Smrg struct AllocationSite {
25ac8e35e1Smrg   u32 id;
26ac8e35e1Smrg   uptr total_size;
27ac8e35e1Smrg   uptr count;
28ac8e35e1Smrg };
29ac8e35e1Smrg 
30ac8e35e1Smrg class HeapProfile {
31ac8e35e1Smrg  public:
HeapProfile()32*760c2415Smrg   HeapProfile() { allocations_.reserve(1024); }
338dd4bdcdSmrg 
ProcessChunk(const AsanChunkView & cv)348dd4bdcdSmrg   void ProcessChunk(const AsanChunkView &cv) {
358dd4bdcdSmrg     if (cv.IsAllocated()) {
368dd4bdcdSmrg       total_allocated_user_size_ += cv.UsedSize();
378dd4bdcdSmrg       total_allocated_count_++;
388dd4bdcdSmrg       u32 id = cv.GetAllocStackId();
398dd4bdcdSmrg       if (id)
408dd4bdcdSmrg         Insert(id, cv.UsedSize());
418dd4bdcdSmrg     } else if (cv.IsQuarantined()) {
428dd4bdcdSmrg       total_quarantined_user_size_ += cv.UsedSize();
438dd4bdcdSmrg       total_quarantined_count_++;
448dd4bdcdSmrg     } else {
458dd4bdcdSmrg       total_other_count_++;
468dd4bdcdSmrg     }
478dd4bdcdSmrg   }
488dd4bdcdSmrg 
Print(uptr top_percent,uptr max_number_of_contexts)498dd4bdcdSmrg   void Print(uptr top_percent, uptr max_number_of_contexts) {
50*760c2415Smrg     Sort(allocations_.data(), allocations_.size(),
518dd4bdcdSmrg          [](const AllocationSite &a, const AllocationSite &b) {
528dd4bdcdSmrg            return a.total_size > b.total_size;
538dd4bdcdSmrg          });
548dd4bdcdSmrg     CHECK(total_allocated_user_size_);
558dd4bdcdSmrg     uptr total_shown = 0;
568dd4bdcdSmrg     Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: "
578dd4bdcdSmrg            "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; "
588dd4bdcdSmrg            "showing top %zd%% (at most %zd unique contexts)\n",
598dd4bdcdSmrg            total_allocated_user_size_, total_allocated_count_,
608dd4bdcdSmrg            total_quarantined_user_size_, total_quarantined_count_,
618dd4bdcdSmrg            total_other_count_, total_allocated_count_ +
628dd4bdcdSmrg            total_quarantined_count_ + total_other_count_, top_percent,
638dd4bdcdSmrg            max_number_of_contexts);
648dd4bdcdSmrg     for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts);
658dd4bdcdSmrg          i++) {
668dd4bdcdSmrg       auto &a = allocations_[i];
678dd4bdcdSmrg       Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size,
688dd4bdcdSmrg              a.total_size * 100 / total_allocated_user_size_, a.count);
698dd4bdcdSmrg       StackDepotGet(a.id).Print();
708dd4bdcdSmrg       total_shown += a.total_size;
718dd4bdcdSmrg       if (total_shown * 100 / total_allocated_user_size_ > top_percent)
728dd4bdcdSmrg         break;
738dd4bdcdSmrg     }
748dd4bdcdSmrg   }
758dd4bdcdSmrg 
768dd4bdcdSmrg  private:
778dd4bdcdSmrg   uptr total_allocated_user_size_ = 0;
788dd4bdcdSmrg   uptr total_allocated_count_ = 0;
798dd4bdcdSmrg   uptr total_quarantined_user_size_ = 0;
808dd4bdcdSmrg   uptr total_quarantined_count_ = 0;
818dd4bdcdSmrg   uptr total_other_count_ = 0;
828dd4bdcdSmrg   InternalMmapVector<AllocationSite> allocations_;
838dd4bdcdSmrg 
Insert(u32 id,uptr size)84ac8e35e1Smrg   void Insert(u32 id, uptr size) {
85ac8e35e1Smrg     // Linear lookup will be good enough for most cases (although not all).
86ac8e35e1Smrg     for (uptr i = 0; i < allocations_.size(); i++) {
87ac8e35e1Smrg       if (allocations_[i].id == id) {
88ac8e35e1Smrg         allocations_[i].total_size += size;
89ac8e35e1Smrg         allocations_[i].count++;
90ac8e35e1Smrg         return;
91ac8e35e1Smrg       }
92ac8e35e1Smrg     }
93ac8e35e1Smrg     allocations_.push_back({id, size, 1});
94ac8e35e1Smrg   }
95ac8e35e1Smrg };
96ac8e35e1Smrg 
ChunkCallback(uptr chunk,void * arg)97ac8e35e1Smrg static void ChunkCallback(uptr chunk, void *arg) {
988dd4bdcdSmrg   reinterpret_cast<HeapProfile*>(arg)->ProcessChunk(
998dd4bdcdSmrg       FindHeapChunkByAllocBeg(chunk));
100ac8e35e1Smrg }
101ac8e35e1Smrg 
MemoryProfileCB(const SuspendedThreadsList & suspended_threads_list,void * argument)102ac8e35e1Smrg static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list,
103ac8e35e1Smrg                             void *argument) {
104ac8e35e1Smrg   HeapProfile hp;
105ac8e35e1Smrg   __lsan::ForEachChunk(ChunkCallback, &hp);
1068dd4bdcdSmrg   uptr *Arg = reinterpret_cast<uptr*>(argument);
1078dd4bdcdSmrg   hp.Print(Arg[0], Arg[1]);
1088dd4bdcdSmrg 
1098dd4bdcdSmrg   if (Verbosity())
1108dd4bdcdSmrg     __asan_print_accumulated_stats();
111ac8e35e1Smrg }
112ac8e35e1Smrg 
113ac8e35e1Smrg }  // namespace __asan
114ac8e35e1Smrg 
1158dd4bdcdSmrg #endif  // CAN_SANITIZE_LEAKS
1168dd4bdcdSmrg 
117ac8e35e1Smrg extern "C" {
118ac8e35e1Smrg SANITIZER_INTERFACE_ATTRIBUTE
__sanitizer_print_memory_profile(uptr top_percent,uptr max_number_of_contexts)1198dd4bdcdSmrg void __sanitizer_print_memory_profile(uptr top_percent,
1208dd4bdcdSmrg                                       uptr max_number_of_contexts) {
1218dd4bdcdSmrg #if CAN_SANITIZE_LEAKS
1228dd4bdcdSmrg   uptr Arg[2];
1238dd4bdcdSmrg   Arg[0] = top_percent;
1248dd4bdcdSmrg   Arg[1] = max_number_of_contexts;
1258dd4bdcdSmrg   __sanitizer::StopTheWorld(__asan::MemoryProfileCB, Arg);
1268dd4bdcdSmrg #endif  // CAN_SANITIZE_LEAKS
127ac8e35e1Smrg }
128ac8e35e1Smrg }  // extern "C"
129