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