1353944Sdim//===-- asan_stats.cpp ----------------------------------------------------===// 2353944Sdim// 3353944Sdim// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4353944Sdim// See https://llvm.org/LICENSE.txt for license information. 5353944Sdim// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6353944Sdim// 7353944Sdim//===----------------------------------------------------------------------===// 8353944Sdim// 9353944Sdim// This file is a part of AddressSanitizer, an address sanity checker. 10353944Sdim// 11353944Sdim// Code related to statistics collected by AddressSanitizer. 12353944Sdim//===----------------------------------------------------------------------===// 13353944Sdim#include "asan_interceptors.h" 14353944Sdim#include "asan_internal.h" 15353944Sdim#include "asan_stats.h" 16353944Sdim#include "asan_thread.h" 17353944Sdim#include "sanitizer_common/sanitizer_allocator_interface.h" 18353944Sdim#include "sanitizer_common/sanitizer_mutex.h" 19353944Sdim#include "sanitizer_common/sanitizer_stackdepot.h" 20353944Sdim 21353944Sdimnamespace __asan { 22353944Sdim 23353944SdimAsanStats::AsanStats() { 24353944Sdim Clear(); 25353944Sdim} 26353944Sdim 27353944Sdimvoid AsanStats::Clear() { 28353944Sdim CHECK(REAL(memset)); 29353944Sdim REAL(memset)(this, 0, sizeof(AsanStats)); 30353944Sdim} 31353944Sdim 32353944Sdimstatic void PrintMallocStatsArray(const char *prefix, 33353944Sdim uptr (&array)[kNumberOfSizeClasses]) { 34353944Sdim Printf("%s", prefix); 35353944Sdim for (uptr i = 0; i < kNumberOfSizeClasses; i++) { 36353944Sdim if (!array[i]) continue; 37353944Sdim Printf("%zu:%zu; ", i, array[i]); 38353944Sdim } 39353944Sdim Printf("\n"); 40353944Sdim} 41353944Sdim 42353944Sdimvoid AsanStats::Print() { 43353944Sdim Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n", 44353944Sdim malloced>>20, malloced_redzones>>20, mallocs); 45353944Sdim Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs); 46353944Sdim Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees); 47353944Sdim Printf("Stats: %zuM really freed by %zu calls\n", 48353944Sdim really_freed>>20, real_frees); 49353944Sdim Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n", 50353944Sdim (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20, 51353944Sdim mmaps, munmaps); 52353944Sdim 53353944Sdim PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); 54353944Sdim Printf("Stats: malloc large: %zu\n", malloc_large); 55353944Sdim} 56353944Sdim 57353944Sdimvoid AsanStats::MergeFrom(const AsanStats *stats) { 58353944Sdim uptr *dst_ptr = reinterpret_cast<uptr*>(this); 59353944Sdim const uptr *src_ptr = reinterpret_cast<const uptr*>(stats); 60353944Sdim uptr num_fields = sizeof(*this) / sizeof(uptr); 61353944Sdim for (uptr i = 0; i < num_fields; i++) 62353944Sdim dst_ptr[i] += src_ptr[i]; 63353944Sdim} 64353944Sdim 65353944Sdimstatic BlockingMutex print_lock(LINKER_INITIALIZED); 66353944Sdim 67353944Sdimstatic AsanStats unknown_thread_stats(LINKER_INITIALIZED); 68353944Sdimstatic AsanStats dead_threads_stats(LINKER_INITIALIZED); 69353944Sdimstatic BlockingMutex dead_threads_stats_lock(LINKER_INITIALIZED); 70353944Sdim// Required for malloc_zone_statistics() on OS X. This can't be stored in 71353944Sdim// per-thread AsanStats. 72353944Sdimstatic uptr max_malloced_memory; 73353944Sdim 74353944Sdimstatic void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) { 75353944Sdim AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg); 76353944Sdim AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base); 77353944Sdim if (AsanThread *t = tctx->thread) 78353944Sdim accumulated_stats->MergeFrom(&t->stats()); 79353944Sdim} 80353944Sdim 81353944Sdimstatic void GetAccumulatedStats(AsanStats *stats) { 82353944Sdim stats->Clear(); 83353944Sdim { 84353944Sdim ThreadRegistryLock l(&asanThreadRegistry()); 85353944Sdim asanThreadRegistry() 86353944Sdim .RunCallbackForEachThreadLocked(MergeThreadStats, stats); 87353944Sdim } 88353944Sdim stats->MergeFrom(&unknown_thread_stats); 89353944Sdim { 90353944Sdim BlockingMutexLock lock(&dead_threads_stats_lock); 91353944Sdim stats->MergeFrom(&dead_threads_stats); 92353944Sdim } 93353944Sdim // This is not very accurate: we may miss allocation peaks that happen 94353944Sdim // between two updates of accumulated_stats_. For more accurate bookkeeping 95353944Sdim // the maximum should be updated on every malloc(), which is unacceptable. 96353944Sdim if (max_malloced_memory < stats->malloced) { 97353944Sdim max_malloced_memory = stats->malloced; 98353944Sdim } 99353944Sdim} 100353944Sdim 101353944Sdimvoid FlushToDeadThreadStats(AsanStats *stats) { 102353944Sdim BlockingMutexLock lock(&dead_threads_stats_lock); 103353944Sdim dead_threads_stats.MergeFrom(stats); 104353944Sdim stats->Clear(); 105353944Sdim} 106353944Sdim 107353944Sdimvoid FillMallocStatistics(AsanMallocStats *malloc_stats) { 108353944Sdim AsanStats stats; 109353944Sdim GetAccumulatedStats(&stats); 110353944Sdim malloc_stats->blocks_in_use = stats.mallocs; 111353944Sdim malloc_stats->size_in_use = stats.malloced; 112353944Sdim malloc_stats->max_size_in_use = max_malloced_memory; 113353944Sdim malloc_stats->size_allocated = stats.mmaped; 114353944Sdim} 115353944Sdim 116353944SdimAsanStats &GetCurrentThreadStats() { 117353944Sdim AsanThread *t = GetCurrentThread(); 118353944Sdim return (t) ? t->stats() : unknown_thread_stats; 119353944Sdim} 120353944Sdim 121353944Sdimstatic void PrintAccumulatedStats() { 122353944Sdim AsanStats stats; 123353944Sdim GetAccumulatedStats(&stats); 124353944Sdim // Use lock to keep reports from mixing up. 125353944Sdim BlockingMutexLock lock(&print_lock); 126353944Sdim stats.Print(); 127353944Sdim StackDepotStats *stack_depot_stats = StackDepotGetStats(); 128353944Sdim Printf("Stats: StackDepot: %zd ids; %zdM allocated\n", 129353944Sdim stack_depot_stats->n_uniq_ids, stack_depot_stats->allocated >> 20); 130353944Sdim PrintInternalAllocatorStats(); 131353944Sdim} 132353944Sdim 133353944Sdim} // namespace __asan 134353944Sdim 135353944Sdim// ---------------------- Interface ---------------- {{{1 136353944Sdimusing namespace __asan; 137353944Sdim 138353944Sdimuptr __sanitizer_get_current_allocated_bytes() { 139353944Sdim AsanStats stats; 140353944Sdim GetAccumulatedStats(&stats); 141353944Sdim uptr malloced = stats.malloced; 142353944Sdim uptr freed = stats.freed; 143353944Sdim // Return sane value if malloced < freed due to racy 144353944Sdim // way we update accumulated stats. 145353944Sdim return (malloced > freed) ? malloced - freed : 1; 146353944Sdim} 147353944Sdim 148353944Sdimuptr __sanitizer_get_heap_size() { 149353944Sdim AsanStats stats; 150353944Sdim GetAccumulatedStats(&stats); 151353944Sdim return stats.mmaped - stats.munmaped; 152353944Sdim} 153353944Sdim 154353944Sdimuptr __sanitizer_get_free_bytes() { 155353944Sdim AsanStats stats; 156353944Sdim GetAccumulatedStats(&stats); 157353944Sdim uptr total_free = stats.mmaped 158353944Sdim - stats.munmaped 159353944Sdim + stats.really_freed; 160353944Sdim uptr total_used = stats.malloced 161353944Sdim + stats.malloced_redzones; 162353944Sdim // Return sane value if total_free < total_used due to racy 163353944Sdim // way we update accumulated stats. 164353944Sdim return (total_free > total_used) ? total_free - total_used : 1; 165353944Sdim} 166353944Sdim 167353944Sdimuptr __sanitizer_get_unmapped_bytes() { 168353944Sdim return 0; 169353944Sdim} 170353944Sdim 171353944Sdimvoid __asan_print_accumulated_stats() { 172353944Sdim PrintAccumulatedStats(); 173353944Sdim} 174