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 "sanitizer_common/sanitizer_common.h"
15 #include "sanitizer_common/sanitizer_stackdepot.h"
16 #include "sanitizer_common/sanitizer_stacktrace.h"
17 #include "sanitizer_common/sanitizer_stoptheworld.h"
18 #include "lsan/lsan_common.h"
19 #include "asan/asan_allocator.h"
21 #if CAN_SANITIZE_LEAKS
25 struct AllocationSite
{
33 HeapProfile() { allocations_
.reserve(1024); }
35 void ProcessChunk(const AsanChunkView
&cv
) {
36 if (cv
.IsAllocated()) {
37 total_allocated_user_size_
+= cv
.UsedSize();
38 total_allocated_count_
++;
39 u32 id
= cv
.GetAllocStackId();
41 Insert(id
, cv
.UsedSize());
42 } else if (cv
.IsQuarantined()) {
43 total_quarantined_user_size_
+= cv
.UsedSize();
44 total_quarantined_count_
++;
50 void Print(uptr top_percent
, uptr max_number_of_contexts
) {
51 Sort(allocations_
.data(), allocations_
.size(),
52 [](const AllocationSite
&a
, const AllocationSite
&b
) {
53 return a
.total_size
> b
.total_size
;
55 CHECK(total_allocated_user_size_
);
57 Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: "
58 "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; "
59 "showing top %zd%% (at most %zd unique contexts)\n",
60 total_allocated_user_size_
, total_allocated_count_
,
61 total_quarantined_user_size_
, total_quarantined_count_
,
62 total_other_count_
, total_allocated_count_
+
63 total_quarantined_count_
+ total_other_count_
, top_percent
,
64 max_number_of_contexts
);
65 for (uptr i
= 0; i
< Min(allocations_
.size(), max_number_of_contexts
);
67 auto &a
= allocations_
[i
];
68 Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a
.total_size
,
69 a
.total_size
* 100 / total_allocated_user_size_
, a
.count
);
70 StackDepotGet(a
.id
).Print();
71 total_shown
+= a
.total_size
;
72 if (total_shown
* 100 / total_allocated_user_size_
> top_percent
)
78 uptr total_allocated_user_size_
= 0;
79 uptr total_allocated_count_
= 0;
80 uptr total_quarantined_user_size_
= 0;
81 uptr total_quarantined_count_
= 0;
82 uptr total_other_count_
= 0;
83 InternalMmapVector
<AllocationSite
> allocations_
;
85 void Insert(u32 id
, uptr size
) {
86 // Linear lookup will be good enough for most cases (although not all).
87 for (uptr i
= 0; i
< allocations_
.size(); i
++) {
88 if (allocations_
[i
].id
== id
) {
89 allocations_
[i
].total_size
+= size
;
90 allocations_
[i
].count
++;
94 allocations_
.push_back({id
, size
, 1});
98 static void ChunkCallback(uptr chunk
, void *arg
) {
99 reinterpret_cast<HeapProfile
*>(arg
)->ProcessChunk(
100 FindHeapChunkByAllocBeg(chunk
));
103 static void MemoryProfileCB(const SuspendedThreadsList
&suspended_threads_list
,
106 __lsan::ForEachChunk(ChunkCallback
, &hp
);
107 uptr
*Arg
= reinterpret_cast<uptr
*>(argument
);
108 hp
.Print(Arg
[0], Arg
[1]);
111 __asan_print_accumulated_stats();
114 } // namespace __asan
116 #endif // CAN_SANITIZE_LEAKS
119 SANITIZER_INTERFACE_ATTRIBUTE
120 void __sanitizer_print_memory_profile(uptr top_percent
,
121 uptr max_number_of_contexts
) {
122 #if CAN_SANITIZE_LEAKS
124 Arg
[0] = top_percent
;
125 Arg
[1] = max_number_of_contexts
;
126 __sanitizer::StopTheWorld(__asan::MemoryProfileCB
, Arg
);
127 #endif // CAN_SANITIZE_LEAKS