1 //===-- asan_memory_profile.cc.cc -----------------------------------------===//
3 // This file is distributed under the University of Illinois Open Source
4 // License. See LICENSE.TXT for details.
6 //===----------------------------------------------------------------------===//
8 // This file is a part of AddressSanitizer, an address sanity checker.
10 // This file implements __sanitizer_print_memory_profile.
11 //===----------------------------------------------------------------------===//
13 #include "sanitizer_common/sanitizer_common.h"
14 #include "sanitizer_common/sanitizer_stackdepot.h"
15 #include "sanitizer_common/sanitizer_stacktrace.h"
16 #include "sanitizer_common/sanitizer_stoptheworld.h"
17 #include "lsan/lsan_common.h"
18 #include "asan/asan_allocator.h"
20 #if CAN_SANITIZE_LEAKS
24 struct AllocationSite
{
32 HeapProfile() : allocations_(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 InternalSort(&allocations_
, 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(const SuspendedThreadsList
&suspended_threads_list
,
105 __lsan::ForEachChunk(ChunkCallback
, &hp
);
106 uptr
*Arg
= reinterpret_cast<uptr
*>(argument
);
107 hp
.Print(Arg
[0], Arg
[1]);
110 __asan_print_accumulated_stats();
113 } // namespace __asan
115 #endif // CAN_SANITIZE_LEAKS
118 SANITIZER_INTERFACE_ATTRIBUTE
119 void __sanitizer_print_memory_profile(uptr top_percent
,
120 uptr max_number_of_contexts
) {
121 #if CAN_SANITIZE_LEAKS
123 Arg
[0] = top_percent
;
124 Arg
[1] = max_number_of_contexts
;
125 __sanitizer::StopTheWorld(__asan::MemoryProfileCB
, Arg
);
126 #endif // CAN_SANITIZE_LEAKS