Home | History | Annotate | Line # | Download | only in asan
asan_stats.cpp revision 1.3
      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.1  mrg   return (malloced > freed) ? malloced - freed : 1;
    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.1  mrg   return (total_free > total_used) ? total_free - total_used : 1;
    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