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