xref: /freebsd/contrib/llvm-project/compiler-rt/lib/asan/asan_stats.cpp (revision 68d75eff68281c1b445e3010bb975eae07aac225)
1*68d75effSDimitry Andric //===-- asan_stats.cpp ----------------------------------------------------===//
2*68d75effSDimitry Andric //
3*68d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*68d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information.
5*68d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*68d75effSDimitry Andric //
7*68d75effSDimitry Andric //===----------------------------------------------------------------------===//
8*68d75effSDimitry Andric //
9*68d75effSDimitry Andric // This file is a part of AddressSanitizer, an address sanity checker.
10*68d75effSDimitry Andric //
11*68d75effSDimitry Andric // Code related to statistics collected by AddressSanitizer.
12*68d75effSDimitry Andric //===----------------------------------------------------------------------===//
13*68d75effSDimitry Andric #include "asan_interceptors.h"
14*68d75effSDimitry Andric #include "asan_internal.h"
15*68d75effSDimitry Andric #include "asan_stats.h"
16*68d75effSDimitry Andric #include "asan_thread.h"
17*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h"
18*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_mutex.h"
19*68d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h"
20*68d75effSDimitry Andric 
21*68d75effSDimitry Andric namespace __asan {
22*68d75effSDimitry Andric 
23*68d75effSDimitry Andric AsanStats::AsanStats() {
24*68d75effSDimitry Andric   Clear();
25*68d75effSDimitry Andric }
26*68d75effSDimitry Andric 
27*68d75effSDimitry Andric void AsanStats::Clear() {
28*68d75effSDimitry Andric   CHECK(REAL(memset));
29*68d75effSDimitry Andric   REAL(memset)(this, 0, sizeof(AsanStats));
30*68d75effSDimitry Andric }
31*68d75effSDimitry Andric 
32*68d75effSDimitry Andric static void PrintMallocStatsArray(const char *prefix,
33*68d75effSDimitry Andric                                   uptr (&array)[kNumberOfSizeClasses]) {
34*68d75effSDimitry Andric   Printf("%s", prefix);
35*68d75effSDimitry Andric   for (uptr i = 0; i < kNumberOfSizeClasses; i++) {
36*68d75effSDimitry Andric     if (!array[i]) continue;
37*68d75effSDimitry Andric     Printf("%zu:%zu; ", i, array[i]);
38*68d75effSDimitry Andric   }
39*68d75effSDimitry Andric   Printf("\n");
40*68d75effSDimitry Andric }
41*68d75effSDimitry Andric 
42*68d75effSDimitry Andric void AsanStats::Print() {
43*68d75effSDimitry Andric   Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n",
44*68d75effSDimitry Andric              malloced>>20, malloced_redzones>>20, mallocs);
45*68d75effSDimitry Andric   Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs);
46*68d75effSDimitry Andric   Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees);
47*68d75effSDimitry Andric   Printf("Stats: %zuM really freed by %zu calls\n",
48*68d75effSDimitry Andric              really_freed>>20, real_frees);
49*68d75effSDimitry Andric   Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n",
50*68d75effSDimitry Andric              (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20,
51*68d75effSDimitry Andric              mmaps, munmaps);
52*68d75effSDimitry Andric 
53*68d75effSDimitry Andric   PrintMallocStatsArray("  mallocs by size class: ", malloced_by_size);
54*68d75effSDimitry Andric   Printf("Stats: malloc large: %zu\n", malloc_large);
55*68d75effSDimitry Andric }
56*68d75effSDimitry Andric 
57*68d75effSDimitry Andric void AsanStats::MergeFrom(const AsanStats *stats) {
58*68d75effSDimitry Andric   uptr *dst_ptr = reinterpret_cast<uptr*>(this);
59*68d75effSDimitry Andric   const uptr *src_ptr = reinterpret_cast<const uptr*>(stats);
60*68d75effSDimitry Andric   uptr num_fields = sizeof(*this) / sizeof(uptr);
61*68d75effSDimitry Andric   for (uptr i = 0; i < num_fields; i++)
62*68d75effSDimitry Andric     dst_ptr[i] += src_ptr[i];
63*68d75effSDimitry Andric }
64*68d75effSDimitry Andric 
65*68d75effSDimitry Andric static BlockingMutex print_lock(LINKER_INITIALIZED);
66*68d75effSDimitry Andric 
67*68d75effSDimitry Andric static AsanStats unknown_thread_stats(LINKER_INITIALIZED);
68*68d75effSDimitry Andric static AsanStats dead_threads_stats(LINKER_INITIALIZED);
69*68d75effSDimitry Andric static BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED);
70*68d75effSDimitry Andric // Required for malloc_zone_statistics() on OS X. This can't be stored in
71*68d75effSDimitry Andric // per-thread AsanStats.
72*68d75effSDimitry Andric static uptr max_malloced_memory;
73*68d75effSDimitry Andric 
74*68d75effSDimitry Andric static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) {
75*68d75effSDimitry Andric   AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg);
76*68d75effSDimitry Andric   AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base);
77*68d75effSDimitry Andric   if (AsanThread *t = tctx->thread)
78*68d75effSDimitry Andric     accumulated_stats->MergeFrom(&t->stats());
79*68d75effSDimitry Andric }
80*68d75effSDimitry Andric 
81*68d75effSDimitry Andric static void GetAccumulatedStats(AsanStats *stats) {
82*68d75effSDimitry Andric   stats->Clear();
83*68d75effSDimitry Andric   {
84*68d75effSDimitry Andric     ThreadRegistryLock l(&asanThreadRegistry());
85*68d75effSDimitry Andric     asanThreadRegistry()
86*68d75effSDimitry Andric         .RunCallbackForEachThreadLocked(MergeThreadStats, stats);
87*68d75effSDimitry Andric   }
88*68d75effSDimitry Andric   stats->MergeFrom(&unknown_thread_stats);
89*68d75effSDimitry Andric   {
90*68d75effSDimitry Andric     BlockingMutexLock lock(&dead_threads_stats_lock);
91*68d75effSDimitry Andric     stats->MergeFrom(&dead_threads_stats);
92*68d75effSDimitry Andric   }
93*68d75effSDimitry Andric   // This is not very accurate: we may miss allocation peaks that happen
94*68d75effSDimitry Andric   // between two updates of accumulated_stats_. For more accurate bookkeeping
95*68d75effSDimitry Andric   // the maximum should be updated on every malloc(), which is unacceptable.
96*68d75effSDimitry Andric   if (max_malloced_memory < stats->malloced) {
97*68d75effSDimitry Andric     max_malloced_memory = stats->malloced;
98*68d75effSDimitry Andric   }
99*68d75effSDimitry Andric }
100*68d75effSDimitry Andric 
101*68d75effSDimitry Andric void FlushToDeadThreadStats(AsanStats *stats) {
102*68d75effSDimitry Andric   BlockingMutexLock lock(&dead_threads_stats_lock);
103*68d75effSDimitry Andric   dead_threads_stats.MergeFrom(stats);
104*68d75effSDimitry Andric   stats->Clear();
105*68d75effSDimitry Andric }
106*68d75effSDimitry Andric 
107*68d75effSDimitry Andric void FillMallocStatistics(AsanMallocStats *malloc_stats) {
108*68d75effSDimitry Andric   AsanStats stats;
109*68d75effSDimitry Andric   GetAccumulatedStats(&stats);
110*68d75effSDimitry Andric   malloc_stats->blocks_in_use = stats.mallocs;
111*68d75effSDimitry Andric   malloc_stats->size_in_use = stats.malloced;
112*68d75effSDimitry Andric   malloc_stats->max_size_in_use = max_malloced_memory;
113*68d75effSDimitry Andric   malloc_stats->size_allocated = stats.mmaped;
114*68d75effSDimitry Andric }
115*68d75effSDimitry Andric 
116*68d75effSDimitry Andric AsanStats &GetCurrentThreadStats() {
117*68d75effSDimitry Andric   AsanThread *t = GetCurrentThread();
118*68d75effSDimitry Andric   return (t) ? t->stats() : unknown_thread_stats;
119*68d75effSDimitry Andric }
120*68d75effSDimitry Andric 
121*68d75effSDimitry Andric static void PrintAccumulatedStats() {
122*68d75effSDimitry Andric   AsanStats stats;
123*68d75effSDimitry Andric   GetAccumulatedStats(&stats);
124*68d75effSDimitry Andric   // Use lock to keep reports from mixing up.
125*68d75effSDimitry Andric   BlockingMutexLock lock(&print_lock);
126*68d75effSDimitry Andric   stats.Print();
127*68d75effSDimitry Andric   StackDepotStats *stack_depot_stats = StackDepotGetStats();
128*68d75effSDimitry Andric   Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
129*68d75effSDimitry Andric          stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20);
130*68d75effSDimitry Andric   PrintInternalAllocatorStats();
131*68d75effSDimitry Andric }
132*68d75effSDimitry Andric 
133*68d75effSDimitry Andric }  // namespace __asan
134*68d75effSDimitry Andric 
135*68d75effSDimitry Andric // ---------------------- Interface ---------------- {{{1
136*68d75effSDimitry Andric using namespace __asan;
137*68d75effSDimitry Andric 
138*68d75effSDimitry Andric uptr __sanitizer_get_current_allocated_bytes() {
139*68d75effSDimitry Andric   AsanStats stats;
140*68d75effSDimitry Andric   GetAccumulatedStats(&stats);
141*68d75effSDimitry Andric   uptr malloced = stats.malloced;
142*68d75effSDimitry Andric   uptr freed = stats.freed;
143*68d75effSDimitry Andric   // Return sane value if malloced < freed due to racy
144*68d75effSDimitry Andric   // way we update accumulated stats.
145*68d75effSDimitry Andric   return (malloced > freed) ? malloced - freed : 1;
146*68d75effSDimitry Andric }
147*68d75effSDimitry Andric 
148*68d75effSDimitry Andric uptr __sanitizer_get_heap_size() {
149*68d75effSDimitry Andric   AsanStats stats;
150*68d75effSDimitry Andric   GetAccumulatedStats(&stats);
151*68d75effSDimitry Andric   return stats.mmaped - stats.munmaped;
152*68d75effSDimitry Andric }
153*68d75effSDimitry Andric 
154*68d75effSDimitry Andric uptr __sanitizer_get_free_bytes() {
155*68d75effSDimitry Andric   AsanStats stats;
156*68d75effSDimitry Andric   GetAccumulatedStats(&stats);
157*68d75effSDimitry Andric   uptr total_free = stats.mmaped
158*68d75effSDimitry Andric                   - stats.munmaped
159*68d75effSDimitry Andric                   + stats.really_freed;
160*68d75effSDimitry Andric   uptr total_used = stats.malloced
161*68d75effSDimitry Andric                   + stats.malloced_redzones;
162*68d75effSDimitry Andric   // Return sane value if total_free < total_used due to racy
163*68d75effSDimitry Andric   // way we update accumulated stats.
164*68d75effSDimitry Andric   return (total_free > total_used) ? total_free - total_used : 1;
165*68d75effSDimitry Andric }
166*68d75effSDimitry Andric 
167*68d75effSDimitry Andric uptr __sanitizer_get_unmapped_bytes() {
168*68d75effSDimitry Andric   return 0;
169*68d75effSDimitry Andric }
170*68d75effSDimitry Andric 
171*68d75effSDimitry Andric void __asan_print_accumulated_stats() {
172*68d75effSDimitry Andric   PrintAccumulatedStats();
173*68d75effSDimitry Andric }
174