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