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