168d75effSDimitry Andric //===-- asan_stats.cpp ----------------------------------------------------===// 268d75effSDimitry Andric // 368d75effSDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 468d75effSDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 568d75effSDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 668d75effSDimitry Andric // 768d75effSDimitry Andric //===----------------------------------------------------------------------===// 868d75effSDimitry Andric // 968d75effSDimitry Andric // This file is a part of AddressSanitizer, an address sanity checker. 1068d75effSDimitry Andric // 1168d75effSDimitry Andric // Code related to statistics collected by AddressSanitizer. 1268d75effSDimitry Andric //===----------------------------------------------------------------------===// 1368d75effSDimitry Andric #include "asan_interceptors.h" 1468d75effSDimitry Andric #include "asan_internal.h" 1568d75effSDimitry Andric #include "asan_stats.h" 1668d75effSDimitry Andric #include "asan_thread.h" 1768d75effSDimitry Andric #include "sanitizer_common/sanitizer_allocator_interface.h" 1868d75effSDimitry Andric #include "sanitizer_common/sanitizer_mutex.h" 1968d75effSDimitry Andric #include "sanitizer_common/sanitizer_stackdepot.h" 2068d75effSDimitry Andric 2168d75effSDimitry Andric namespace __asan { 2268d75effSDimitry Andric 2368d75effSDimitry Andric AsanStats::AsanStats() { 2468d75effSDimitry Andric Clear(); 2568d75effSDimitry Andric } 2668d75effSDimitry Andric 2768d75effSDimitry Andric void AsanStats::Clear() { 2868d75effSDimitry Andric CHECK(REAL(memset)); 2968d75effSDimitry Andric REAL(memset)(this, 0, sizeof(AsanStats)); 3068d75effSDimitry Andric } 3168d75effSDimitry Andric 3268d75effSDimitry Andric static void PrintMallocStatsArray(const char *prefix, 3368d75effSDimitry Andric uptr (&array)[kNumberOfSizeClasses]) { 3468d75effSDimitry Andric Printf("%s", prefix); 3568d75effSDimitry Andric for (uptr i = 0; i < kNumberOfSizeClasses; i++) { 3668d75effSDimitry Andric if (!array[i]) continue; 3768d75effSDimitry Andric Printf("%zu:%zu; ", i, array[i]); 3868d75effSDimitry Andric } 3968d75effSDimitry Andric Printf("\n"); 4068d75effSDimitry Andric } 4168d75effSDimitry Andric 4268d75effSDimitry Andric void AsanStats::Print() { 4368d75effSDimitry Andric Printf("Stats: %zuM malloced (%zuM for red zones) by %zu calls\n", 4468d75effSDimitry Andric malloced>>20, malloced_redzones>>20, mallocs); 4568d75effSDimitry Andric Printf("Stats: %zuM realloced by %zu calls\n", realloced>>20, reallocs); 4668d75effSDimitry Andric Printf("Stats: %zuM freed by %zu calls\n", freed>>20, frees); 4768d75effSDimitry Andric Printf("Stats: %zuM really freed by %zu calls\n", 4868d75effSDimitry Andric really_freed>>20, real_frees); 4968d75effSDimitry Andric Printf("Stats: %zuM (%zuM-%zuM) mmaped; %zu maps, %zu unmaps\n", 5068d75effSDimitry Andric (mmaped-munmaped)>>20, mmaped>>20, munmaped>>20, 5168d75effSDimitry Andric mmaps, munmaps); 5268d75effSDimitry Andric 5368d75effSDimitry Andric PrintMallocStatsArray(" mallocs by size class: ", malloced_by_size); 5468d75effSDimitry Andric Printf("Stats: malloc large: %zu\n", malloc_large); 5568d75effSDimitry Andric } 5668d75effSDimitry Andric 5768d75effSDimitry Andric void AsanStats::MergeFrom(const AsanStats *stats) { 5868d75effSDimitry Andric uptr *dst_ptr = reinterpret_cast<uptr*>(this); 5968d75effSDimitry Andric const uptr *src_ptr = reinterpret_cast<const uptr*>(stats); 6068d75effSDimitry Andric uptr num_fields = sizeof(*this) / sizeof(uptr); 6168d75effSDimitry Andric for (uptr i = 0; i < num_fields; i++) 6268d75effSDimitry Andric dst_ptr[i] += src_ptr[i]; 6368d75effSDimitry Andric } 6468d75effSDimitry Andric 65349cc55cSDimitry Andric static Mutex print_lock; 6668d75effSDimitry Andric 6768d75effSDimitry Andric static AsanStats unknown_thread_stats(LINKER_INITIALIZED); 6868d75effSDimitry Andric static AsanStats dead_threads_stats(LINKER_INITIALIZED); 69349cc55cSDimitry Andric static Mutex dead_threads_stats_lock; 7068d75effSDimitry Andric // Required for malloc_zone_statistics() on OS X. This can't be stored in 7168d75effSDimitry Andric // per-thread AsanStats. 7268d75effSDimitry Andric static uptr max_malloced_memory; 7368d75effSDimitry Andric 7468d75effSDimitry Andric static void MergeThreadStats(ThreadContextBase *tctx_base, void *arg) { 7568d75effSDimitry Andric AsanStats *accumulated_stats = reinterpret_cast<AsanStats*>(arg); 7668d75effSDimitry Andric AsanThreadContext *tctx = static_cast<AsanThreadContext*>(tctx_base); 7768d75effSDimitry Andric if (AsanThread *t = tctx->thread) 7868d75effSDimitry Andric accumulated_stats->MergeFrom(&t->stats()); 7968d75effSDimitry Andric } 8068d75effSDimitry Andric 8168d75effSDimitry Andric static void GetAccumulatedStats(AsanStats *stats) { 8268d75effSDimitry Andric stats->Clear(); 8368d75effSDimitry Andric { 8468d75effSDimitry Andric ThreadRegistryLock l(&asanThreadRegistry()); 8568d75effSDimitry Andric asanThreadRegistry() 8668d75effSDimitry Andric .RunCallbackForEachThreadLocked(MergeThreadStats, stats); 8768d75effSDimitry Andric } 8868d75effSDimitry Andric stats->MergeFrom(&unknown_thread_stats); 8968d75effSDimitry Andric { 90349cc55cSDimitry Andric Lock lock(&dead_threads_stats_lock); 9168d75effSDimitry Andric stats->MergeFrom(&dead_threads_stats); 9268d75effSDimitry Andric } 9368d75effSDimitry Andric // This is not very accurate: we may miss allocation peaks that happen 9468d75effSDimitry Andric // between two updates of accumulated_stats_. For more accurate bookkeeping 9568d75effSDimitry Andric // the maximum should be updated on every malloc(), which is unacceptable. 9668d75effSDimitry Andric if (max_malloced_memory < stats->malloced) { 9768d75effSDimitry Andric max_malloced_memory = stats->malloced; 9868d75effSDimitry Andric } 9968d75effSDimitry Andric } 10068d75effSDimitry Andric 10168d75effSDimitry Andric void FlushToDeadThreadStats(AsanStats *stats) { 102349cc55cSDimitry Andric Lock lock(&dead_threads_stats_lock); 10368d75effSDimitry Andric dead_threads_stats.MergeFrom(stats); 10468d75effSDimitry Andric stats->Clear(); 10568d75effSDimitry Andric } 10668d75effSDimitry Andric 10768d75effSDimitry Andric void FillMallocStatistics(AsanMallocStats *malloc_stats) { 10868d75effSDimitry Andric AsanStats stats; 10968d75effSDimitry Andric GetAccumulatedStats(&stats); 11068d75effSDimitry Andric malloc_stats->blocks_in_use = stats.mallocs; 11168d75effSDimitry Andric malloc_stats->size_in_use = stats.malloced; 11268d75effSDimitry Andric malloc_stats->max_size_in_use = max_malloced_memory; 11368d75effSDimitry Andric malloc_stats->size_allocated = stats.mmaped; 11468d75effSDimitry Andric } 11568d75effSDimitry Andric 11668d75effSDimitry Andric AsanStats &GetCurrentThreadStats() { 11768d75effSDimitry Andric AsanThread *t = GetCurrentThread(); 11868d75effSDimitry Andric return (t) ? t->stats() : unknown_thread_stats; 11968d75effSDimitry Andric } 12068d75effSDimitry Andric 12168d75effSDimitry Andric static void PrintAccumulatedStats() { 12268d75effSDimitry Andric AsanStats stats; 12368d75effSDimitry Andric GetAccumulatedStats(&stats); 12468d75effSDimitry Andric // Use lock to keep reports from mixing up. 125349cc55cSDimitry Andric Lock lock(&print_lock); 12668d75effSDimitry Andric stats.Print(); 127349cc55cSDimitry Andric StackDepotStats stack_depot_stats = StackDepotGetStats(); 12868d75effSDimitry Andric Printf("Stats: StackDepot: %zd ids; %zdM allocated\n", 129349cc55cSDimitry Andric stack_depot_stats.n_uniq_ids, stack_depot_stats.allocated >> 20); 13068d75effSDimitry Andric PrintInternalAllocatorStats(); 13168d75effSDimitry Andric } 13268d75effSDimitry Andric 13368d75effSDimitry Andric } // namespace __asan 13468d75effSDimitry Andric 13568d75effSDimitry Andric // ---------------------- Interface ---------------- {{{1 13668d75effSDimitry Andric using namespace __asan; 13768d75effSDimitry Andric 13868d75effSDimitry Andric uptr __sanitizer_get_current_allocated_bytes() { 13968d75effSDimitry Andric AsanStats stats; 14068d75effSDimitry Andric GetAccumulatedStats(&stats); 14168d75effSDimitry Andric uptr malloced = stats.malloced; 14268d75effSDimitry Andric uptr freed = stats.freed; 14368d75effSDimitry Andric // Return sane value if malloced < freed due to racy 14468d75effSDimitry Andric // way we update accumulated stats. 145*5f757f3fSDimitry Andric return (malloced > freed) ? malloced - freed : 0; 14668d75effSDimitry Andric } 14768d75effSDimitry Andric 14868d75effSDimitry Andric uptr __sanitizer_get_heap_size() { 14968d75effSDimitry Andric AsanStats stats; 15068d75effSDimitry Andric GetAccumulatedStats(&stats); 15168d75effSDimitry Andric return stats.mmaped - stats.munmaped; 15268d75effSDimitry Andric } 15368d75effSDimitry Andric 15468d75effSDimitry Andric uptr __sanitizer_get_free_bytes() { 15568d75effSDimitry Andric AsanStats stats; 15668d75effSDimitry Andric GetAccumulatedStats(&stats); 15768d75effSDimitry Andric uptr total_free = stats.mmaped 15868d75effSDimitry Andric - stats.munmaped 15968d75effSDimitry Andric + stats.really_freed; 16068d75effSDimitry Andric uptr total_used = stats.malloced 16168d75effSDimitry Andric + stats.malloced_redzones; 16268d75effSDimitry Andric // Return sane value if total_free < total_used due to racy 16368d75effSDimitry Andric // way we update accumulated stats. 164*5f757f3fSDimitry Andric return (total_free > total_used) ? total_free - total_used : 0; 16568d75effSDimitry Andric } 16668d75effSDimitry Andric 16768d75effSDimitry Andric uptr __sanitizer_get_unmapped_bytes() { 16868d75effSDimitry Andric return 0; 16968d75effSDimitry Andric } 17068d75effSDimitry Andric 17168d75effSDimitry Andric void __asan_print_accumulated_stats() { 17268d75effSDimitry Andric PrintAccumulatedStats(); 17368d75effSDimitry Andric } 174