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