Home | History | Annotate | Line # | Download | only in asan
      1 //===-- asan_memory_profile.cc.cc -----------------------------------------===//
      2 //
      3 //                     The LLVM Compiler Infrastructure
      4 //
      5 // This file is distributed under the University of Illinois Open Source
      6 // License. See LICENSE.TXT for details.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 //
     10 // This file is a part of AddressSanitizer, an address sanity checker.
     11 //
     12 // This file implements __sanitizer_print_memory_profile.
     13 //===----------------------------------------------------------------------===//
     14 
     15 #include "sanitizer_common/sanitizer_common.h"
     16 #include "sanitizer_common/sanitizer_stackdepot.h"
     17 #include "sanitizer_common/sanitizer_stacktrace.h"
     18 #include "sanitizer_common/sanitizer_stoptheworld.h"
     19 #include "lsan/lsan_common.h"
     20 #include "asan/asan_allocator.h"
     21 
     22 #if CAN_SANITIZE_LEAKS
     23 
     24 namespace __asan {
     25 
     26 struct AllocationSite {
     27   u32 id;
     28   uptr total_size;
     29   uptr count;
     30 };
     31 
     32 class HeapProfile {
     33  public:
     34   HeapProfile() { allocations_.reserve(1024); }
     35 
     36   void ProcessChunk(const AsanChunkView &cv) {
     37     if (cv.IsAllocated()) {
     38       total_allocated_user_size_ += cv.UsedSize();
     39       total_allocated_count_++;
     40       u32 id = cv.GetAllocStackId();
     41       if (id)
     42         Insert(id, cv.UsedSize());
     43     } else if (cv.IsQuarantined()) {
     44       total_quarantined_user_size_ += cv.UsedSize();
     45       total_quarantined_count_++;
     46     } else {
     47       total_other_count_++;
     48     }
     49   }
     50 
     51   void Print(uptr top_percent, uptr max_number_of_contexts) {
     52     Sort(allocations_.data(), allocations_.size(),
     53          [](const AllocationSite &a, const AllocationSite &b) {
     54            return a.total_size > b.total_size;
     55          });
     56     CHECK(total_allocated_user_size_);
     57     uptr total_shown = 0;
     58     Printf("Live Heap Allocations: %zd bytes in %zd chunks; quarantined: "
     59            "%zd bytes in %zd chunks; %zd other chunks; total chunks: %zd; "
     60            "showing top %zd%% (at most %zd unique contexts)\n",
     61            total_allocated_user_size_, total_allocated_count_,
     62            total_quarantined_user_size_, total_quarantined_count_,
     63            total_other_count_, total_allocated_count_ +
     64            total_quarantined_count_ + total_other_count_, top_percent,
     65            max_number_of_contexts);
     66     for (uptr i = 0; i < Min(allocations_.size(), max_number_of_contexts);
     67          i++) {
     68       auto &a = allocations_[i];
     69       Printf("%zd byte(s) (%zd%%) in %zd allocation(s)\n", a.total_size,
     70              a.total_size * 100 / total_allocated_user_size_, a.count);
     71       StackDepotGet(a.id).Print();
     72       total_shown += a.total_size;
     73       if (total_shown * 100 / total_allocated_user_size_ > top_percent)
     74         break;
     75     }
     76   }
     77 
     78  private:
     79   uptr total_allocated_user_size_ = 0;
     80   uptr total_allocated_count_ = 0;
     81   uptr total_quarantined_user_size_ = 0;
     82   uptr total_quarantined_count_ = 0;
     83   uptr total_other_count_ = 0;
     84   InternalMmapVector<AllocationSite> allocations_;
     85 
     86   void Insert(u32 id, uptr size) {
     87     // Linear lookup will be good enough for most cases (although not all).
     88     for (uptr i = 0; i < allocations_.size(); i++) {
     89       if (allocations_[i].id == id) {
     90         allocations_[i].total_size += size;
     91         allocations_[i].count++;
     92         return;
     93       }
     94     }
     95     allocations_.push_back({id, size, 1});
     96   }
     97 };
     98 
     99 static void ChunkCallback(uptr chunk, void *arg) {
    100   reinterpret_cast<HeapProfile*>(arg)->ProcessChunk(
    101       FindHeapChunkByAllocBeg(chunk));
    102 }
    103 
    104 static void MemoryProfileCB(const SuspendedThreadsList &suspended_threads_list,
    105                             void *argument) {
    106   HeapProfile hp;
    107   __lsan::ForEachChunk(ChunkCallback, &hp);
    108   uptr *Arg = reinterpret_cast<uptr*>(argument);
    109   hp.Print(Arg[0], Arg[1]);
    110 
    111   if (Verbosity())
    112     __asan_print_accumulated_stats();
    113 }
    114 
    115 }  // namespace __asan
    116 
    117 #endif  // CAN_SANITIZE_LEAKS
    118 
    119 extern "C" {
    120 SANITIZER_INTERFACE_ATTRIBUTE
    121 void __sanitizer_print_memory_profile(uptr top_percent,
    122                                       uptr max_number_of_contexts) {
    123 #if CAN_SANITIZE_LEAKS
    124   uptr Arg[2];
    125   Arg[0] = top_percent;
    126   Arg[1] = max_number_of_contexts;
    127   __sanitizer::StopTheWorld(__asan::MemoryProfileCB, Arg);
    128 #endif  // CAN_SANITIZE_LEAKS
    129 }
    130 }  // extern "C"
    131