1 //===-- asan_memory_profile.cpp ----------------------------------------===//
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-exception
7 //===----------------------------------------------------------------------===//
9 // This file is a part of AddressSanitizer, an address sanity checker.
11 // This file implements __sanitizer_print_memory_profile.
12 //===----------------------------------------------------------------------===//
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"
20 #if CAN_SANITIZE_LEAKS
24 struct AllocationSite
{
32 HeapProfile() { allocations_
.reserve(1024); }
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();
40 Insert(id
, cv
.UsedSize());
41 } else if (cv
.IsQuarantined()) {
42 total_quarantined_user_size_
+= cv
.UsedSize();
43 total_quarantined_count_
++;
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
;
54 CHECK(total_allocated_user_size_
);
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
);
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
)
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_
;
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
++;
93 allocations_
.push_back({id
, size
, 1});
97 static void ChunkCallback(uptr chunk
, void *arg
) {
98 reinterpret_cast<HeapProfile
*>(arg
)->ProcessChunk(
99 FindHeapChunkByAllocBeg(chunk
));
102 static void MemoryProfileCB(uptr top_percent
, uptr max_number_of_contexts
) {
104 __lsan::LockAllocator();
105 __lsan::ForEachChunk(ChunkCallback
, &hp
);
106 __lsan::UnlockAllocator();
107 hp
.Print(top_percent
, max_number_of_contexts
);
110 __asan_print_accumulated_stats();
112 } // namespace __asan
114 #endif // CAN_SANITIZE_LEAKS
117 SANITIZER_INTERFACE_ATTRIBUTE
118 void __sanitizer_print_memory_profile(uptr top_percent
,
119 uptr max_number_of_contexts
) {
120 #if CAN_SANITIZE_LEAKS
121 __asan::MemoryProfileCB(top_percent
, max_number_of_contexts
);
122 #endif // CAN_SANITIZE_LEAKS