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