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 #include <vector>
      9 
     10 #include "base/process/process_metrics.h"
     11 #include "base/trace_event/process_memory_totals.h"
     12 #include "base/trace_event/trace_event_argument.h"
     13 #include "build/build_config.h"
     14 
     15 #if defined(OS_POSIX)
     16 #include <sys/mman.h>
     17 #endif
     18 
     19 namespace base {
     20 namespace trace_event {
     21 
     22 namespace {
     23 
     24 const char kEdgeTypeOwnership[] = "ownership";
     25 
     26 std::string GetSharedGlobalAllocatorDumpName(
     27     const MemoryAllocatorDumpGuid& guid) {
     28   return "global/" + guid.ToString();
     29 }
     30 
     31 }  // namespace
     32 
     33 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
     34 // static
     35 size_t ProcessMemoryDump::CountResidentBytes(void* start_address,
     36                                              size_t mapped_size) {
     37   const size_t page_size = GetPageSize();
     38   const uintptr_t start_pointer = reinterpret_cast<uintptr_t>(start_address);
     39   DCHECK_EQ(0u, start_pointer % page_size);
     40 
     41   // This function allocates a char vector of size number of pages in the given
     42   // mapped_size. To avoid allocating a large array, the memory is split into
     43   // chunks. Maximum size of vector allocated, will be
     44   // kPageChunkSize / page_size.
     45   const size_t kMaxChunkSize = 32 * 1024 * 1024;
     46   size_t offset = 0;
     47   size_t total_resident_size = 0;
     48   int result = 0;
     49   while (offset < mapped_size) {
     50     void* chunk_start = reinterpret_cast<void*>(start_pointer + offset);
     51     const size_t chunk_size = std::min(mapped_size - offset, kMaxChunkSize);
     52     const size_t page_count = (chunk_size + page_size - 1) / page_size;
     53     size_t resident_page_count = 0;
     54 
     55 #if defined(OS_MACOSX) || defined(OS_IOS)
     56     std::vector<char> vec(page_count + 1);
     57     // mincore in MAC does not fail with EAGAIN.
     58     result = mincore(chunk_start, chunk_size, vec.data());
     59     if (result)
     60       break;
     61 
     62     for (size_t i = 0; i < page_count; i++)
     63       resident_page_count += vec[i] & MINCORE_INCORE ? 1 : 0;
     64 #else   // defined(OS_MACOSX) || defined(OS_IOS)
     65     std::vector<unsigned char> vec(page_count + 1);
     66     int error_counter = 0;
     67     // HANDLE_EINTR tries for 100 times. So following the same pattern.
     68     do {
     69       result = mincore(chunk_start, chunk_size, vec.data());
     70     } while (result == -1 && errno == EAGAIN && error_counter++ < 100);
     71     if (result)
     72       break;
     73 
     74     for (size_t i = 0; i < page_count; i++)
     75       resident_page_count += vec[i];
     76 #endif  // defined(OS_MACOSX) || defined(OS_IOS)
     77 
     78     total_resident_size += resident_page_count * page_size;
     79     offset += kMaxChunkSize;
     80   }
     81 
     82   DCHECK_EQ(0, result);
     83   if (result) {
     84     total_resident_size = 0;
     85     LOG(ERROR) << "mincore() call failed. The resident size is invalid";
     86   }
     87   return total_resident_size;
     88 }
     89 #endif  // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
     90 
     91 ProcessMemoryDump::ProcessMemoryDump(
     92     const scoped_refptr<MemoryDumpSessionState>& session_state)
     93     : has_process_totals_(false),
     94       has_process_mmaps_(false),
     95       session_state_(session_state) {
     96 }
     97 
     98 ProcessMemoryDump::~ProcessMemoryDump() {
     99 }
    100 
    101 MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
    102     const std::string& absolute_name) {
    103   MemoryAllocatorDump* mad = new MemoryAllocatorDump(absolute_name, this);
    104   AddAllocatorDumpInternal(mad);  // Takes ownership of |mad|.
    105   return mad;
    106 }
    107 
    108 MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
    109     const std::string& absolute_name,
    110     const MemoryAllocatorDumpGuid& guid) {
    111   MemoryAllocatorDump* mad = new MemoryAllocatorDump(absolute_name, this, guid);
    112   AddAllocatorDumpInternal(mad);  // Takes ownership of |mad|.
    113   return mad;
    114 }
    115 
    116 void ProcessMemoryDump::AddAllocatorDumpInternal(MemoryAllocatorDump* mad) {
    117   DCHECK_EQ(0ul, allocator_dumps_.count(mad->absolute_name()));
    118   allocator_dumps_storage_.push_back(mad);
    119   allocator_dumps_[mad->absolute_name()] = mad;
    120 }
    121 
    122 MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump(
    123     const std::string& absolute_name) const {
    124   auto it = allocator_dumps_.find(absolute_name);
    125   return it == allocator_dumps_.end() ? nullptr : it->second;
    126 }
    127 
    128 MemoryAllocatorDump* ProcessMemoryDump::GetOrCreateAllocatorDump(
    129     const std::string& absolute_name) {
    130   MemoryAllocatorDump* mad = GetAllocatorDump(absolute_name);
    131   return mad ? mad : CreateAllocatorDump(absolute_name);
    132 }
    133 
    134 MemoryAllocatorDump* ProcessMemoryDump::CreateSharedGlobalAllocatorDump(
    135     const MemoryAllocatorDumpGuid& guid) {
    136   // A shared allocator dump can be shared within a process and the guid could
    137   // have been created already.
    138   MemoryAllocatorDump* allocator_dump = GetSharedGlobalAllocatorDump(guid);
    139   return allocator_dump ? allocator_dump
    140                         : CreateAllocatorDump(
    141                               GetSharedGlobalAllocatorDumpName(guid), guid);
    142 }
    143 
    144 MemoryAllocatorDump* ProcessMemoryDump::GetSharedGlobalAllocatorDump(
    145     const MemoryAllocatorDumpGuid& guid) const {
    146   return GetAllocatorDump(GetSharedGlobalAllocatorDumpName(guid));
    147 }
    148 
    149 void ProcessMemoryDump::AddHeapDump(const std::string& absolute_name,
    150                                     scoped_refptr<TracedValue> heap_dump) {
    151   DCHECK_EQ(0ul, heap_dumps_.count(absolute_name));
    152   heap_dumps_[absolute_name] = heap_dump;
    153 }
    154 
    155 void ProcessMemoryDump::Clear() {
    156   if (has_process_totals_) {
    157     process_totals_.Clear();
    158     has_process_totals_ = false;
    159   }
    160 
    161   if (has_process_mmaps_) {
    162     process_mmaps_.Clear();
    163     has_process_mmaps_ = false;
    164   }
    165 
    166   allocator_dumps_storage_.clear();
    167   allocator_dumps_.clear();
    168   allocator_dumps_edges_.clear();
    169   heap_dumps_.clear();
    170 }
    171 
    172 void ProcessMemoryDump::TakeAllDumpsFrom(ProcessMemoryDump* other) {
    173   DCHECK(!other->has_process_totals() && !other->has_process_mmaps());
    174 
    175   // Moves the ownership of all MemoryAllocatorDump(s) contained in |other|
    176   // into this ProcessMemoryDump.
    177   for (MemoryAllocatorDump* mad : other->allocator_dumps_storage_) {
    178     // Check that we don't merge duplicates.
    179     DCHECK_EQ(0ul, allocator_dumps_.count(mad->absolute_name()));
    180     allocator_dumps_storage_.push_back(mad);
    181     allocator_dumps_[mad->absolute_name()] = mad;
    182   }
    183   other->allocator_dumps_storage_.weak_clear();
    184   other->allocator_dumps_.clear();
    185 
    186   // Move all the edges.
    187   allocator_dumps_edges_.insert(allocator_dumps_edges_.end(),
    188                                 other->allocator_dumps_edges_.begin(),
    189                                 other->allocator_dumps_edges_.end());
    190   other->allocator_dumps_edges_.clear();
    191 
    192   heap_dumps_.insert(other->heap_dumps_.begin(), other->heap_dumps_.end());
    193   other->heap_dumps_.clear();
    194 }
    195 
    196 void ProcessMemoryDump::AsValueInto(TracedValue* value) const {
    197   if (has_process_totals_) {
    198     value->BeginDictionary("process_totals");
    199     process_totals_.AsValueInto(value);
    200     value->EndDictionary();
    201   }
    202 
    203   if (has_process_mmaps_) {
    204     value->BeginDictionary("process_mmaps");
    205     process_mmaps_.AsValueInto(value);
    206     value->EndDictionary();
    207   }
    208 
    209   if (allocator_dumps_storage_.size() > 0) {
    210     value->BeginDictionary("allocators");
    211     for (const MemoryAllocatorDump* allocator_dump : allocator_dumps_storage_)
    212       allocator_dump->AsValueInto(value);
    213     value->EndDictionary();
    214   }
    215 
    216   if (heap_dumps_.size() > 0) {
    217     value->BeginDictionary("heaps");
    218     for (const auto& name_and_dump : heap_dumps_)
    219       value->SetValueWithCopiedName(name_and_dump.first, *name_and_dump.second);
    220     value->EndDictionary();  // "heaps"
    221   }
    222 
    223   value->BeginArray("allocators_graph");
    224   for (const MemoryAllocatorDumpEdge& edge : allocator_dumps_edges_) {
    225     value->BeginDictionary();
    226     value->SetString("source", edge.source.ToString());
    227     value->SetString("target", edge.target.ToString());
    228     value->SetInteger("importance", edge.importance);
    229     value->SetString("type", edge.type);
    230     value->EndDictionary();
    231   }
    232   value->EndArray();
    233 }
    234 
    235 void ProcessMemoryDump::AddOwnershipEdge(const MemoryAllocatorDumpGuid& source,
    236                                          const MemoryAllocatorDumpGuid& target,
    237                                          int importance) {
    238   allocator_dumps_edges_.push_back(
    239       {source, target, importance, kEdgeTypeOwnership});
    240 }
    241 
    242 void ProcessMemoryDump::AddOwnershipEdge(
    243     const MemoryAllocatorDumpGuid& source,
    244     const MemoryAllocatorDumpGuid& target) {
    245   AddOwnershipEdge(source, target, 0 /* importance */);
    246 }
    247 
    248 void ProcessMemoryDump::AddSuballocation(const MemoryAllocatorDumpGuid& source,
    249                                          const std::string& target_node_name) {
    250   std::string child_mad_name = target_node_name + "/__" + source.ToString();
    251   MemoryAllocatorDump* target_child_mad = CreateAllocatorDump(child_mad_name);
    252   AddOwnershipEdge(source, target_child_mad->guid());
    253 }
    254 
    255 }  // namespace trace_event
    256 }  // namespace base
    257