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