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