Home | History | Annotate | Download | only in trace_event
      1 // Copyright 2015 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/trace_event/process_memory_dump.h"
      6 
      7 #include <errno.h>
      8 
      9 #include <vector>
     10 
     11 #include "base/memory/ptr_util.h"
     12 #include "base/process/process_metrics.h"
     13 #include "base/strings/stringprintf.h"
     14 #include "base/trace_event/heap_profiler_heap_dump_writer.h"
     15 #include "base/trace_event/memory_infra_background_whitelist.h"
     16 #include "base/trace_event/process_memory_totals.h"
     17 #include "base/trace_event/trace_event_argument.h"
     18 #include "build/build_config.h"
     19 
     20 #if defined(OS_IOS)
     21 #include <mach/vm_page_size.h>
     22 #endif
     23 
     24 #if defined(OS_POSIX)
     25 #include <sys/mman.h>
     26 #endif
     27 
     28 #if defined(OS_WIN)
     29 #include <Psapi.h>
     30 #endif
     31 
     32 namespace base {
     33 namespace trace_event {
     34 
     35 namespace {
     36 
     37 const char kEdgeTypeOwnership[] = "ownership";
     38 
     39 std::string GetSharedGlobalAllocatorDumpName(
     40     const MemoryAllocatorDumpGuid& guid) {
     41   return "global/" + guid.ToString();
     42 }
     43 
     44 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
     45 size_t GetSystemPageCount(size_t mapped_size, size_t page_size) {
     46   return (mapped_size + page_size - 1) / page_size;
     47 }
     48 #endif
     49 
     50 }  // namespace
     51 
     52 // static
     53 bool ProcessMemoryDump::is_black_hole_non_fatal_for_testing_ = false;
     54 
     55 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
     56 // static
     57 size_t ProcessMemoryDump::GetSystemPageSize() {
     58 #if defined(OS_IOS)
     59   // On iOS, getpagesize() returns the user page sizes, but for allocating
     60   // arrays for mincore(), kernel page sizes is needed. Use vm_kernel_page_size
     61   // as recommended by Apple, https://forums.developer.apple.com/thread/47532/.
     62   // Refer to http://crbug.com/542671 and Apple rdar://23651782
     63   return vm_kernel_page_size;
     64 #else
     65   return base::GetPageSize();
     66 #endif  // defined(OS_IOS)
     67 }
     68 
     69 // static
     70 size_t ProcessMemoryDump::CountResidentBytes(void* start_address,
     71                                              size_t mapped_size) {
     72   const size_t page_size = GetSystemPageSize();
     73   const uintptr_t start_pointer = reinterpret_cast<uintptr_t>(start_address);
     74   DCHECK_EQ(0u, start_pointer % page_size);
     75 
     76   size_t offset = 0;
     77   size_t total_resident_size = 0;
     78   bool failure = false;
     79 
     80   // An array as large as number of pages in memory segment needs to be passed
     81   // to the query function. To avoid allocating a large array, the given block
     82   // of memory is split into chunks of size |kMaxChunkSize|.
     83   const size_t kMaxChunkSize = 8 * 1024 * 1024;
     84   size_t max_vec_size =
     85       GetSystemPageCount(std::min(mapped_size, kMaxChunkSize), page_size);
     86 #if defined(OS_MACOSX) || defined(OS_IOS)
     87   std::unique_ptr<char[]> vec(new char[max_vec_size]);
     88 #elif defined(OS_WIN)
     89   std::unique_ptr<PSAPI_WORKING_SET_EX_INFORMATION[]> vec(
     90       new PSAPI_WORKING_SET_EX_INFORMATION[max_vec_size]);
     91 #elif defined(OS_POSIX)
     92   std::unique_ptr<unsigned char[]> vec(new unsigned char[max_vec_size]);
     93 #endif
     94 
     95   while (offset < mapped_size) {
     96     uintptr_t chunk_start = (start_pointer + offset);
     97     const size_t chunk_size = std::min(mapped_size - offset, kMaxChunkSize);
     98     const size_t page_count = GetSystemPageCount(chunk_size, page_size);
     99     size_t resident_page_count = 0;
    100 
    101 #if defined(OS_MACOSX) || defined(OS_IOS)
    102     // mincore in MAC does not fail with EAGAIN.
    103     failure =
    104         !!mincore(reinterpret_cast<void*>(chunk_start), chunk_size, vec.get());
    105     for (size_t i = 0; i < page_count; i++)
    106       resident_page_count += vec[i] & MINCORE_INCORE ? 1 : 0;
    107 #elif defined(OS_WIN)
    108     for (size_t i = 0; i < page_count; i++) {
    109       vec[i].VirtualAddress =
    110           reinterpret_cast<void*>(chunk_start + i * page_size);
    111     }
    112     DWORD vec_size = static_cast<DWORD>(
    113         page_count * sizeof(PSAPI_WORKING_SET_EX_INFORMATION));
    114     failure = !QueryWorkingSetEx(GetCurrentProcess(), vec.get(), vec_size);
    115 
    116     for (size_t i = 0; i < page_count; i++)
    117       resident_page_count += vec[i].VirtualAttributes.Valid;
    118 #elif defined(OS_POSIX)
    119     int error_counter = 0;
    120     int result = 0;
    121     // HANDLE_EINTR tries for 100 times. So following the same pattern.
    122     do {
    123       result =
    124           mincore(reinterpret_cast<void*>(chunk_start), chunk_size, vec.get());
    125     } while (result == -1 && errno == EAGAIN && error_counter++ < 100);
    126     failure = !!result;
    127 
    128     for (size_t i = 0; i < page_count; i++)
    129       resident_page_count += vec[i] & 1;
    130 #endif
    131 
    132     if (failure)
    133       break;
    134 
    135     total_resident_size += resident_page_count * page_size;
    136     offset += kMaxChunkSize;
    137   }
    138 
    139   DCHECK(!failure);
    140   if (failure) {
    141     total_resident_size = 0;
    142     LOG(ERROR) << "CountResidentBytes failed. The resident size is invalid";
    143   }
    144   return total_resident_size;
    145 }
    146 #endif  // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
    147 
    148 ProcessMemoryDump::ProcessMemoryDump(
    149     scoped_refptr<MemoryDumpSessionState> session_state,
    150     const MemoryDumpArgs& dump_args)
    151     : has_process_totals_(false),
    152       has_process_mmaps_(false),
    153       session_state_(std::move(session_state)),
    154       dump_args_(dump_args) {}
    155 
    156 ProcessMemoryDump::~ProcessMemoryDump() {}
    157 
    158 MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
    159     const std::string& absolute_name) {
    160   return AddAllocatorDumpInternal(
    161       MakeUnique<MemoryAllocatorDump>(absolute_name, this));
    162 }
    163 
    164 MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
    165     const std::string& absolute_name,
    166     const MemoryAllocatorDumpGuid& guid) {
    167   return AddAllocatorDumpInternal(
    168       MakeUnique<MemoryAllocatorDump>(absolute_name, this, guid));
    169 }
    170 
    171 MemoryAllocatorDump* ProcessMemoryDump::AddAllocatorDumpInternal(
    172     std::unique_ptr<MemoryAllocatorDump> mad) {
    173   // In background mode return the black hole dump, if invalid dump name is
    174   // given.
    175   if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND &&
    176       !IsMemoryAllocatorDumpNameWhitelisted(mad->absolute_name())) {
    177     return GetBlackHoleMad();
    178   }
    179 
    180   auto insertion_result = allocator_dumps_.insert(
    181       std::make_pair(mad->absolute_name(), std::move(mad)));
    182   MemoryAllocatorDump* inserted_mad = insertion_result.first->second.get();
    183   DCHECK(insertion_result.second) << "Duplicate name: "
    184                                   << inserted_mad->absolute_name();
    185   return inserted_mad;
    186 }
    187 
    188 MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump(
    189     const std::string& absolute_name) const {
    190   auto it = allocator_dumps_.find(absolute_name);
    191   if (it != allocator_dumps_.end())
    192     return it->second.get();
    193   if (black_hole_mad_)
    194     return black_hole_mad_.get();
    195   return nullptr;
    196 }
    197 
    198 MemoryAllocatorDump* ProcessMemoryDump::GetOrCreateAllocatorDump(
    199     const std::string& absolute_name) {
    200   MemoryAllocatorDump* mad = GetAllocatorDump(absolute_name);
    201   return mad ? mad : CreateAllocatorDump(absolute_name);
    202 }
    203 
    204 MemoryAllocatorDump* ProcessMemoryDump::CreateSharedGlobalAllocatorDump(
    205     const MemoryAllocatorDumpGuid& guid) {
    206   // Global dumps are disabled in background mode.
    207   if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND)
    208     return GetBlackHoleMad();
    209 
    210   // A shared allocator dump can be shared within a process and the guid could
    211   // have been created already.
    212   MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid);
    213   if (mad) {
    214     // The weak flag is cleared because this method should create a non-weak
    215     // dump.
    216     mad->clear_flags(MemoryAllocatorDump::Flags::WEAK);
    217     return mad;
    218   }
    219   return CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid);
    220 }
    221 
    222 MemoryAllocatorDump* ProcessMemoryDump::CreateWeakSharedGlobalAllocatorDump(
    223     const MemoryAllocatorDumpGuid& guid) {
    224   // Global dumps are disabled in background mode.
    225   if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND)
    226     return GetBlackHoleMad();
    227 
    228   MemoryAllocatorDump* mad = GetSharedGlobalAllocatorDump(guid);
    229   if (mad)
    230     return mad;
    231   mad = CreateAllocatorDump(GetSharedGlobalAllocatorDumpName(guid), guid);
    232   mad->set_flags(MemoryAllocatorDump::Flags::WEAK);
    233   return mad;
    234 }
    235 
    236 MemoryAllocatorDump* ProcessMemoryDump::GetSharedGlobalAllocatorDump(
    237     const MemoryAllocatorDumpGuid& guid) const {
    238   return GetAllocatorDump(GetSharedGlobalAllocatorDumpName(guid));
    239 }
    240 
    241 void ProcessMemoryDump::DumpHeapUsage(
    242     const base::hash_map<base::trace_event::AllocationContext,
    243         base::trace_event::AllocationMetrics>& metrics_by_context,
    244     base::trace_event::TraceEventMemoryOverhead& overhead,
    245     const char* allocator_name) {
    246   if (!metrics_by_context.empty()) {
    247     DCHECK_EQ(0ul, heap_dumps_.count(allocator_name));
    248     std::unique_ptr<TracedValue> heap_dump = ExportHeapDump(
    249         metrics_by_context, *session_state());
    250     heap_dumps_[allocator_name] = std::move(heap_dump);
    251   }
    252 
    253   std::string base_name = base::StringPrintf("tracing/heap_profiler_%s",
    254                                              allocator_name);
    255   overhead.DumpInto(base_name.c_str(), this);
    256 }
    257 
    258 void ProcessMemoryDump::Clear() {
    259   if (has_process_totals_) {
    260     process_totals_.Clear();
    261     has_process_totals_ = false;
    262   }
    263 
    264   if (has_process_mmaps_) {
    265     process_mmaps_.Clear();
    266     has_process_mmaps_ = false;
    267   }
    268 
    269   allocator_dumps_.clear();
    270   allocator_dumps_edges_.clear();
    271   heap_dumps_.clear();
    272 }
    273 
    274 void ProcessMemoryDump::TakeAllDumpsFrom(ProcessMemoryDump* other) {
    275   DCHECK(!other->has_process_totals() && !other->has_process_mmaps());
    276 
    277   // Moves the ownership of all MemoryAllocatorDump(s) contained in |other|
    278   // into this ProcessMemoryDump, checking for duplicates.
    279   for (auto& it : other->allocator_dumps_)
    280     AddAllocatorDumpInternal(std::move(it.second));
    281   other->allocator_dumps_.clear();
    282 
    283   // Move all the edges.
    284   allocator_dumps_edges_.insert(allocator_dumps_edges_.end(),
    285                                 other->allocator_dumps_edges_.begin(),
    286                                 other->allocator_dumps_edges_.end());
    287   other->allocator_dumps_edges_.clear();
    288 
    289   for (auto& it : other->heap_dumps_) {
    290     DCHECK_EQ(0ul, heap_dumps_.count(it.first));
    291     heap_dumps_.insert(std::make_pair(it.first, std::move(it.second)));
    292   }
    293   other->heap_dumps_.clear();
    294 }
    295 
    296 void ProcessMemoryDump::AsValueInto(TracedValue* value) const {
    297   if (has_process_totals_) {
    298     value->BeginDictionary("process_totals");
    299     process_totals_.AsValueInto(value);
    300     value->EndDictionary();
    301   }
    302 
    303   if (has_process_mmaps_) {
    304     value->BeginDictionary("process_mmaps");
    305     process_mmaps_.AsValueInto(value);
    306     value->EndDictionary();
    307   }
    308 
    309   if (allocator_dumps_.size() > 0) {
    310     value->BeginDictionary("allocators");
    311     for (const auto& allocator_dump_it : allocator_dumps_)
    312       allocator_dump_it.second->AsValueInto(value);
    313     value->EndDictionary();
    314   }
    315 
    316   if (heap_dumps_.size() > 0) {
    317     value->BeginDictionary("heaps");
    318     for (const auto& name_and_dump : heap_dumps_)
    319       value->SetValueWithCopiedName(name_and_dump.first, *name_and_dump.second);
    320     value->EndDictionary();  // "heaps"
    321   }
    322 
    323   value->BeginArray("allocators_graph");
    324   for (const MemoryAllocatorDumpEdge& edge : allocator_dumps_edges_) {
    325     value->BeginDictionary();
    326     value->SetString("source", edge.source.ToString());
    327     value->SetString("target", edge.target.ToString());
    328     value->SetInteger("importance", edge.importance);
    329     value->SetString("type", edge.type);
    330     value->EndDictionary();
    331   }
    332   value->EndArray();
    333 }
    334 
    335 void ProcessMemoryDump::AddOwnershipEdge(const MemoryAllocatorDumpGuid& source,
    336                                          const MemoryAllocatorDumpGuid& target,
    337                                          int importance) {
    338   allocator_dumps_edges_.push_back(
    339       {source, target, importance, kEdgeTypeOwnership});
    340 }
    341 
    342 void ProcessMemoryDump::AddOwnershipEdge(
    343     const MemoryAllocatorDumpGuid& source,
    344     const MemoryAllocatorDumpGuid& target) {
    345   AddOwnershipEdge(source, target, 0 /* importance */);
    346 }
    347 
    348 void ProcessMemoryDump::AddSuballocation(const MemoryAllocatorDumpGuid& source,
    349                                          const std::string& target_node_name) {
    350   // Do not create new dumps for suballocations in background mode.
    351   if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND)
    352     return;
    353 
    354   std::string child_mad_name = target_node_name + "/__" + source.ToString();
    355   MemoryAllocatorDump* target_child_mad = CreateAllocatorDump(child_mad_name);
    356   AddOwnershipEdge(source, target_child_mad->guid());
    357 }
    358 
    359 MemoryAllocatorDump* ProcessMemoryDump::GetBlackHoleMad() {
    360   DCHECK(is_black_hole_non_fatal_for_testing_);
    361   if (!black_hole_mad_)
    362     black_hole_mad_.reset(new MemoryAllocatorDump("discarded", this));
    363   return black_hole_mad_.get();
    364 }
    365 
    366 }  // namespace trace_event
    367 }  // namespace base
    368