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