blob: 63d1340e42e3222c3ee99e92a7e676279cffb8a1 [file] [log] [blame]
primianof11ccf52015-02-17 23:21:191// 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
mostynbd01708232015-11-05 11:03:067#include <errno.h>
dcheng093de9b2016-04-04 21:25:518
thestigdb54eaf2015-10-13 06:19:289#include <vector>
10
dcheng093de9b2016-04-04 21:25:5111#include "base/memory/ptr_util.h"
jbroman59915fe2015-10-22 18:11:3312#include "base/process/process_metrics.h"
bashib873c0d42016-05-12 05:41:0413#include "base/strings/stringprintf.h"
14#include "base/trace_event/heap_profiler_heap_dump_writer.h"
ssid448e5ed2016-06-04 12:40:5115#include "base/trace_event/memory_infra_background_whitelist.h"
primianoc96eca72015-02-20 16:41:1416#include "base/trace_event/process_memory_totals.h"
17#include "base/trace_event/trace_event_argument.h"
avibd1ed052015-12-24 04:03:4418#include "build/build_config.h"
primianof11ccf52015-02-17 23:21:1919
pkl0b19e32e2016-03-16 16:57:2920#if defined(OS_IOS)
sdefresne8bb015f2016-09-27 14:39:3721#include <mach/vm_page_size.h>
pkl0b19e32e2016-03-16 16:57:2922#endif
23
ssida3fc37f2015-10-12 17:36:5524#if defined(OS_POSIX)
25#include <sys/mman.h>
26#endif
27
ssidb39972df2016-01-21 13:58:5528#if defined(OS_WIN)
29#include <Psapi.h>
30#endif
31
primianof11ccf52015-02-17 23:21:1932namespace base {
33namespace trace_event {
34
primiano2d1b2f1b2015-05-28 12:34:4935namespace {
thestigdb54eaf2015-10-13 06:19:2836
primiano2d1b2f1b2015-05-28 12:34:4937const char kEdgeTypeOwnership[] = "ownership";
primiano3b4bd6b02015-06-03 09:52:5238
39std::string GetSharedGlobalAllocatorDumpName(
40 const MemoryAllocatorDumpGuid& guid) {
41 return "global/" + guid.ToString();
primiano2d1b2f1b2015-05-28 12:34:4942}
thestigdb54eaf2015-10-13 06:19:2843
ssid22b50162016-02-02 14:33:1744#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
45size_t GetSystemPageCount(size_t mapped_size, size_t page_size) {
46 return (mapped_size + page_size - 1) / page_size;
47}
48#endif
49
primiano3b4bd6b02015-06-03 09:52:5250} // namespace
primiano2d1b2f1b2015-05-28 12:34:4951
ssid448e5ed2016-06-04 12:40:5152// static
53bool ProcessMemoryDump::is_black_hole_non_fatal_for_testing_ = false;
54
ssida3fc37f2015-10-12 17:36:5555#if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
56// static
pkl0b19e32e2016-03-16 16:57:2957size_t ProcessMemoryDump::GetSystemPageSize() {
58#if defined(OS_IOS)
59 // On iOS, getpagesize() returns the user page sizes, but for allocating
sdefresne8bb015f2016-09-27 14:39:3760 // 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
pkl0b19e32e2016-03-16 16:57:2965 return base::GetPageSize();
sdefresne8bb015f2016-09-27 14:39:3766#endif // defined(OS_IOS)
pkl0b19e32e2016-03-16 16:57:2967}
68
69// static
ssida3fc37f2015-10-12 17:36:5570size_t ProcessMemoryDump::CountResidentBytes(void* start_address,
71 size_t mapped_size) {
pkl0b19e32e2016-03-16 16:57:2972 const size_t page_size = GetSystemPageSize();
ssida3fc37f2015-10-12 17:36:5573 const uintptr_t start_pointer = reinterpret_cast<uintptr_t>(start_address);
74 DCHECK_EQ(0u, start_pointer % page_size);
75
ssida3fc37f2015-10-12 17:36:5576 size_t offset = 0;
77 size_t total_resident_size = 0;
ssidb39972df2016-01-21 13:58:5578 bool failure = false;
ssid22b50162016-02-02 14:33:1779
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)
dcheng093de9b2016-04-04 21:25:5187 std::unique_ptr<char[]> vec(new char[max_vec_size]);
ssid22b50162016-02-02 14:33:1788#elif defined(OS_WIN)
dcheng093de9b2016-04-04 21:25:5189 std::unique_ptr<PSAPI_WORKING_SET_EX_INFORMATION[]> vec(
ssid22b50162016-02-02 14:33:1790 new PSAPI_WORKING_SET_EX_INFORMATION[max_vec_size]);
91#elif defined(OS_POSIX)
dcheng093de9b2016-04-04 21:25:5192 std::unique_ptr<unsigned char[]> vec(new unsigned char[max_vec_size]);
ssid22b50162016-02-02 14:33:1793#endif
94
ssida3fc37f2015-10-12 17:36:5595 while (offset < mapped_size) {
ssidb39972df2016-01-21 13:58:5596 uintptr_t chunk_start = (start_pointer + offset);
ssida3fc37f2015-10-12 17:36:5597 const size_t chunk_size = std::min(mapped_size - offset, kMaxChunkSize);
ssid22b50162016-02-02 14:33:1798 const size_t page_count = GetSystemPageCount(chunk_size, page_size);
ssida3fc37f2015-10-12 17:36:5599 size_t resident_page_count = 0;
100
101#if defined(OS_MACOSX) || defined(OS_IOS)
ssid999ca54e2015-10-15 15:26:41102 // mincore in MAC does not fail with EAGAIN.
ssidb39972df2016-01-21 13:58:55103 failure =
ssid22b50162016-02-02 14:33:17104 !!mincore(reinterpret_cast<void*>(chunk_start), chunk_size, vec.get());
ssida3fc37f2015-10-12 17:36:55105 for (size_t i = 0; i < page_count; i++)
106 resident_page_count += vec[i] & MINCORE_INCORE ? 1 : 0;
ssidb39972df2016-01-21 13:58:55107#elif defined(OS_WIN)
ssidb39972df2016-01-21 13:58:55108 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));
ssid22b50162016-02-02 14:33:17114 failure = !QueryWorkingSetEx(GetCurrentProcess(), vec.get(), vec_size);
ssidb39972df2016-01-21 13:58:55115
116 for (size_t i = 0; i < page_count; i++)
117 resident_page_count += vec[i].VirtualAttributes.Valid;
118#elif defined(OS_POSIX)
ssid999ca54e2015-10-15 15:26:41119 int error_counter = 0;
ssidb39972df2016-01-21 13:58:55120 int result = 0;
ssid999ca54e2015-10-15 15:26:41121 // HANDLE_EINTR tries for 100 times. So following the same pattern.
122 do {
ssidb39972df2016-01-21 13:58:55123 result =
ssid22b50162016-02-02 14:33:17124 mincore(reinterpret_cast<void*>(chunk_start), chunk_size, vec.get());
ssid999ca54e2015-10-15 15:26:41125 } while (result == -1 && errno == EAGAIN && error_counter++ < 100);
ssidb39972df2016-01-21 13:58:55126 failure = !!result;
ssid999ca54e2015-10-15 15:26:41127
ssida3fc37f2015-10-12 17:36:55128 for (size_t i = 0; i < page_count; i++)
ssid22b50162016-02-02 14:33:17129 resident_page_count += vec[i] & 1;
ssidb39972df2016-01-21 13:58:55130#endif
131
132 if (failure)
133 break;
ssida3fc37f2015-10-12 17:36:55134
135 total_resident_size += resident_page_count * page_size;
136 offset += kMaxChunkSize;
137 }
ssid999ca54e2015-10-15 15:26:41138
ssidb39972df2016-01-21 13:58:55139 DCHECK(!failure);
140 if (failure) {
ssid999ca54e2015-10-15 15:26:41141 total_resident_size = 0;
ssidb39972df2016-01-21 13:58:55142 LOG(ERROR) << "CountResidentBytes failed. The resident size is invalid";
ssid999ca54e2015-10-15 15:26:41143 }
ssida3fc37f2015-10-12 17:36:55144 return total_resident_size;
145}
146#endif // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
147
primiano2de25ee62015-04-21 06:09:08148ProcessMemoryDump::ProcessMemoryDump(
ssid448e5ed2016-06-04 12:40:51149 scoped_refptr<MemoryDumpSessionState> session_state,
150 const MemoryDumpArgs& dump_args)
primiano2de25ee62015-04-21 06:09:08151 : has_process_totals_(false),
152 has_process_mmaps_(false),
ssid448e5ed2016-06-04 12:40:51153 session_state_(std::move(session_state)),
154 dump_args_(dump_args) {}
primianof11ccf52015-02-17 23:21:19155
primianofbc69782016-02-22 17:23:34156ProcessMemoryDump::~ProcessMemoryDump() {}
primianof11ccf52015-02-17 23:21:19157
primiano13cf4d02015-03-19 16:24:38158MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
primiano08425112015-04-28 17:10:39159 const std::string& absolute_name) {
primianofbc69782016-02-22 17:23:34160 return AddAllocatorDumpInternal(
riceaec7c3997e2016-09-13 04:10:11161 MakeUnique<MemoryAllocatorDump>(absolute_name, this));
primiano13cf4d02015-03-19 16:24:38162}
163
primiano673eda62015-05-29 13:55:31164MemoryAllocatorDump* ProcessMemoryDump::CreateAllocatorDump(
165 const std::string& absolute_name,
166 const MemoryAllocatorDumpGuid& guid) {
primianofbc69782016-02-22 17:23:34167 return AddAllocatorDumpInternal(
riceaec7c3997e2016-09-13 04:10:11168 MakeUnique<MemoryAllocatorDump>(absolute_name, this, guid));
primiano673eda62015-05-29 13:55:31169}
170
primianofbc69782016-02-22 17:23:34171MemoryAllocatorDump* ProcessMemoryDump::AddAllocatorDumpInternal(
dcheng093de9b2016-04-04 21:25:51172 std::unique_ptr<MemoryAllocatorDump> mad) {
ssid448e5ed2016-06-04 12:40:51173 // 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
primianofbc69782016-02-22 17:23:34180 auto insertion_result = allocator_dumps_.insert(
181 std::make_pair(mad->absolute_name(), std::move(mad)));
dskibad20680c2016-04-29 20:40:52182 MemoryAllocatorDump* inserted_mad = insertion_result.first->second.get();
183 DCHECK(insertion_result.second) << "Duplicate name: "
184 << inserted_mad->absolute_name();
185 return inserted_mad;
primiano673eda62015-05-29 13:55:31186}
187
primiano13cf4d02015-03-19 16:24:38188MemoryAllocatorDump* ProcessMemoryDump::GetAllocatorDump(
primiano08425112015-04-28 17:10:39189 const std::string& absolute_name) const {
190 auto it = allocator_dumps_.find(absolute_name);
ssid448e5ed2016-06-04 12:40:51191 if (it != allocator_dumps_.end())
192 return it->second.get();
193 if (black_hole_mad_)
194 return black_hole_mad_.get();
195 return nullptr;
primiano13cf4d02015-03-19 16:24:38196}
197
primiano96baab5e2015-10-05 22:49:10198MemoryAllocatorDump* ProcessMemoryDump::GetOrCreateAllocatorDump(
199 const std::string& absolute_name) {
200 MemoryAllocatorDump* mad = GetAllocatorDump(absolute_name);
thestigdb54eaf2015-10-13 06:19:28201 return mad ? mad : CreateAllocatorDump(absolute_name);
primiano96baab5e2015-10-05 22:49:10202}
203
primiano3b4bd6b02015-06-03 09:52:52204MemoryAllocatorDump* ProcessMemoryDump::CreateSharedGlobalAllocatorDump(
205 const MemoryAllocatorDumpGuid& guid) {
ssid448e5ed2016-06-04 12:40:51206 // Global dumps are disabled in background mode.
207 if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND)
208 return GetBlackHoleMad();
209
ssid1050d802015-07-28 20:28:14210 // A shared allocator dump can be shared within a process and the guid could
211 // have been created already.
ssida937635d2016-01-21 13:41:35212 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
222MemoryAllocatorDump* ProcessMemoryDump::CreateWeakSharedGlobalAllocatorDump(
223 const MemoryAllocatorDumpGuid& guid) {
ssid448e5ed2016-06-04 12:40:51224 // Global dumps are disabled in background mode.
225 if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND)
226 return GetBlackHoleMad();
227
ssida937635d2016-01-21 13:41:35228 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;
primiano3b4bd6b02015-06-03 09:52:52234}
235
236MemoryAllocatorDump* ProcessMemoryDump::GetSharedGlobalAllocatorDump(
237 const MemoryAllocatorDumpGuid& guid) const {
238 return GetAllocatorDump(GetSharedGlobalAllocatorDumpName(guid));
239}
240
bashib873c0d42016-05-12 05:41:04241void 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()) {
bashifa5b57e2016-05-30 01:51:57247 DCHECK_EQ(0ul, heap_dumps_.count(allocator_name));
bashib873c0d42016-05-12 05:41:04248 std::unique_ptr<TracedValue> heap_dump = ExportHeapDump(
249 metrics_by_context, *session_state());
bashifa5b57e2016-05-30 01:51:57250 heap_dumps_[allocator_name] = std::move(heap_dump);
bashib873c0d42016-05-12 05:41:04251 }
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
primiano139c6fe2015-05-26 19:27:04258void 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
primiano139c6fe2015-05-26 19:27:04269 allocator_dumps_.clear();
primiano2d1b2f1b2015-05-28 12:34:49270 allocator_dumps_edges_.clear();
ruuda51d9e662015-10-27 13:20:12271 heap_dumps_.clear();
primiano139c6fe2015-05-26 19:27:04272}
273
primianoc7336082015-05-21 10:07:28274void ProcessMemoryDump::TakeAllDumpsFrom(ProcessMemoryDump* other) {
primianoc7336082015-05-21 10:07:28275 DCHECK(!other->has_process_totals() && !other->has_process_mmaps());
276
277 // Moves the ownership of all MemoryAllocatorDump(s) contained in |other|
primianofbc69782016-02-22 17:23:34278 // into this ProcessMemoryDump, checking for duplicates.
279 for (auto& it : other->allocator_dumps_)
280 AddAllocatorDumpInternal(std::move(it.second));
primianoc7336082015-05-21 10:07:28281 other->allocator_dumps_.clear();
primiano2d1b2f1b2015-05-28 12:34:49282
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();
ruuda51d9e662015-10-27 13:20:12288
primianocb1afb32016-02-29 20:46:05289 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 }
ruuda51d9e662015-10-27 13:20:12293 other->heap_dumps_.clear();
primianoc7336082015-05-21 10:07:28294}
295
primianoc96eca72015-02-20 16:41:14296void ProcessMemoryDump::AsValueInto(TracedValue* value) const {
primianoc96eca72015-02-20 16:41:14297 if (has_process_totals_) {
298 value->BeginDictionary("process_totals");
299 process_totals_.AsValueInto(value);
300 value->EndDictionary();
301 }
primiano2d1b2f1b2015-05-28 12:34:49302
primiano5a4899572015-02-24 17:52:32303 if (has_process_mmaps_) {
304 value->BeginDictionary("process_mmaps");
305 process_mmaps_.AsValueInto(value);
306 value->EndDictionary();
307 }
primiano2d1b2f1b2015-05-28 12:34:49308
primianofbc69782016-02-22 17:23:34309 if (allocator_dumps_.size() > 0) {
primiano13cf4d02015-03-19 16:24:38310 value->BeginDictionary("allocators");
primianofbc69782016-02-22 17:23:34311 for (const auto& allocator_dump_it : allocator_dumps_)
312 allocator_dump_it.second->AsValueInto(value);
primiano13cf4d02015-03-19 16:24:38313 value->EndDictionary();
314 }
primiano2d1b2f1b2015-05-28 12:34:49315
ruuda51d9e662015-10-27 13:20:12316 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
primiano2d1b2f1b2015-05-28 12:34:49323 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
primiano3b4bd6b02015-06-03 09:52:52335void ProcessMemoryDump::AddOwnershipEdge(const MemoryAllocatorDumpGuid& source,
336 const MemoryAllocatorDumpGuid& target,
primiano2d1b2f1b2015-05-28 12:34:49337 int importance) {
338 allocator_dumps_edges_.push_back(
339 {source, target, importance, kEdgeTypeOwnership});
340}
341
primiano3b4bd6b02015-06-03 09:52:52342void ProcessMemoryDump::AddOwnershipEdge(
343 const MemoryAllocatorDumpGuid& source,
344 const MemoryAllocatorDumpGuid& target) {
primiano2d1b2f1b2015-05-28 12:34:49345 AddOwnershipEdge(source, target, 0 /* importance */);
primianof11ccf52015-02-17 23:21:19346}
347
primiano3b4bd6b02015-06-03 09:52:52348void ProcessMemoryDump::AddSuballocation(const MemoryAllocatorDumpGuid& source,
349 const std::string& target_node_name) {
ssid448e5ed2016-06-04 12:40:51350 // Do not create new dumps for suballocations in background mode.
351 if (dump_args_.level_of_detail == MemoryDumpLevelOfDetail::BACKGROUND)
352 return;
353
primiano3b4bd6b02015-06-03 09:52:52354 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
ssid448e5ed2016-06-04 12:40:51359MemoryAllocatorDump* 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
primianof11ccf52015-02-17 23:21:19366} // namespace trace_event
367} // namespace base