Home | History | Annotate | Line # | Download | only in asan
asan_stats.cpp revision 1.1.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.1.1.2  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.1.1.2  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.1.1.2  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.1.1.2  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.1.1.2  mrg   Lock lock(&print_lock);
    126      1.1  mrg   stats.Print();
    127  1.1.1.2  mrg   StackDepotStats stack_depot_stats = StackDepotGetStats();
    128      1.1  mrg   Printf("Stats: StackDepot: %zd ids; %zdM allocated\n",
    129  1.1.1.2  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.1.3  mrg   return (malloced > freed) ? malloced - freed : 0;
    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.1.3  mrg   return (total_free > total_used) ? total_free - total_used : 0;
    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