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