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 <stddef.h>
8
9 #include "base/memory/aligned_memory.h"
10 #include "base/memory/ptr_util.h"
11 #include "base/process/process_metrics.h"
12 #include "base/trace_event/memory_allocator_dump_guid.h"
13 #include "base/trace_event/memory_infra_background_whitelist.h"
14 #include "base/trace_event/trace_event_argument.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace base {
18 namespace trace_event {
19
20 namespace {
21
22 const MemoryDumpArgs kDetailedDumpArgs = {MemoryDumpLevelOfDetail::DETAILED};
23 const char* const kTestDumpNameWhitelist[] = {
24 "Whitelisted/TestName", "Whitelisted/TestName_0x?",
25 "Whitelisted/0x?/TestName", nullptr};
26
GetHeapDump(const ProcessMemoryDump & pmd,const char * name)27 TracedValue* GetHeapDump(const ProcessMemoryDump& pmd, const char* name) {
28 auto it = pmd.heap_dumps().find(name);
29 return it == pmd.heap_dumps().end() ? nullptr : it->second.get();
30 }
31
32 } // namespace
33
TEST(ProcessMemoryDumpTest,Clear)34 TEST(ProcessMemoryDumpTest, Clear) {
35 std::unique_ptr<ProcessMemoryDump> pmd1(
36 new ProcessMemoryDump(nullptr, kDetailedDumpArgs));
37 pmd1->CreateAllocatorDump("mad1");
38 pmd1->CreateAllocatorDump("mad2");
39 ASSERT_FALSE(pmd1->allocator_dumps().empty());
40
41 pmd1->process_totals()->set_resident_set_bytes(42);
42 pmd1->set_has_process_totals();
43
44 pmd1->process_mmaps()->AddVMRegion(ProcessMemoryMaps::VMRegion());
45 pmd1->set_has_process_mmaps();
46
47 pmd1->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
48 MemoryAllocatorDumpGuid(4242));
49
50 MemoryAllocatorDumpGuid shared_mad_guid1(1);
51 MemoryAllocatorDumpGuid shared_mad_guid2(2);
52 pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
53 pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid2);
54
55 pmd1->Clear();
56 ASSERT_TRUE(pmd1->allocator_dumps().empty());
57 ASSERT_TRUE(pmd1->allocator_dumps_edges().empty());
58 ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad1"));
59 ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
60 ASSERT_FALSE(pmd1->has_process_totals());
61 ASSERT_FALSE(pmd1->has_process_mmaps());
62 ASSERT_TRUE(pmd1->process_mmaps()->vm_regions().empty());
63 ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
64 ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
65
66 // Check that calling AsValueInto() doesn't cause a crash.
67 std::unique_ptr<TracedValue> traced_value(new TracedValue);
68 pmd1->AsValueInto(traced_value.get());
69
70 // Check that the pmd can be reused and behaves as expected.
71 auto* mad1 = pmd1->CreateAllocatorDump("mad1");
72 auto* mad3 = pmd1->CreateAllocatorDump("mad3");
73 auto* shared_mad1 = pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
74 auto* shared_mad2 =
75 pmd1->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid2);
76 ASSERT_EQ(4u, pmd1->allocator_dumps().size());
77 ASSERT_EQ(mad1, pmd1->GetAllocatorDump("mad1"));
78 ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
79 ASSERT_EQ(mad3, pmd1->GetAllocatorDump("mad3"));
80 ASSERT_EQ(shared_mad1, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
81 ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
82 ASSERT_EQ(shared_mad2, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
83 ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad2->flags());
84
85 traced_value.reset(new TracedValue);
86 pmd1->AsValueInto(traced_value.get());
87
88 pmd1.reset();
89 }
90
TEST(ProcessMemoryDumpTest,TakeAllDumpsFrom)91 TEST(ProcessMemoryDumpTest, TakeAllDumpsFrom) {
92 std::unique_ptr<TracedValue> traced_value(new TracedValue);
93 hash_map<AllocationContext, AllocationMetrics> metrics_by_context;
94 metrics_by_context[AllocationContext()] = { 1, 1 };
95 TraceEventMemoryOverhead overhead;
96
97 scoped_refptr<MemoryDumpSessionState> session_state =
98 new MemoryDumpSessionState;
99 session_state->SetStackFrameDeduplicator(
100 WrapUnique(new StackFrameDeduplicator));
101 session_state->SetTypeNameDeduplicator(
102 WrapUnique(new TypeNameDeduplicator));
103 std::unique_ptr<ProcessMemoryDump> pmd1(
104 new ProcessMemoryDump(session_state.get(), kDetailedDumpArgs));
105 auto* mad1_1 = pmd1->CreateAllocatorDump("pmd1/mad1");
106 auto* mad1_2 = pmd1->CreateAllocatorDump("pmd1/mad2");
107 pmd1->AddOwnershipEdge(mad1_1->guid(), mad1_2->guid());
108 pmd1->DumpHeapUsage(metrics_by_context, overhead, "pmd1/heap_dump1");
109 pmd1->DumpHeapUsage(metrics_by_context, overhead, "pmd1/heap_dump2");
110
111 std::unique_ptr<ProcessMemoryDump> pmd2(
112 new ProcessMemoryDump(session_state.get(), kDetailedDumpArgs));
113 auto* mad2_1 = pmd2->CreateAllocatorDump("pmd2/mad1");
114 auto* mad2_2 = pmd2->CreateAllocatorDump("pmd2/mad2");
115 pmd2->AddOwnershipEdge(mad2_1->guid(), mad2_2->guid());
116 pmd2->DumpHeapUsage(metrics_by_context, overhead, "pmd2/heap_dump1");
117 pmd2->DumpHeapUsage(metrics_by_context, overhead, "pmd2/heap_dump2");
118
119 MemoryAllocatorDumpGuid shared_mad_guid1(1);
120 MemoryAllocatorDumpGuid shared_mad_guid2(2);
121 auto* shared_mad1 = pmd2->CreateSharedGlobalAllocatorDump(shared_mad_guid1);
122 auto* shared_mad2 =
123 pmd2->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid2);
124
125 pmd1->TakeAllDumpsFrom(pmd2.get());
126
127 // Make sure that pmd2 is empty but still usable after it has been emptied.
128 ASSERT_TRUE(pmd2->allocator_dumps().empty());
129 ASSERT_TRUE(pmd2->allocator_dumps_edges().empty());
130 ASSERT_TRUE(pmd2->heap_dumps().empty());
131 pmd2->CreateAllocatorDump("pmd2/this_mad_stays_with_pmd2");
132 ASSERT_EQ(1u, pmd2->allocator_dumps().size());
133 ASSERT_EQ(1u, pmd2->allocator_dumps().count("pmd2/this_mad_stays_with_pmd2"));
134 pmd2->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
135 MemoryAllocatorDumpGuid(4242));
136
137 // Check that calling AsValueInto() doesn't cause a crash.
138 pmd2->AsValueInto(traced_value.get());
139
140 // Free the |pmd2| to check that the memory ownership of the two MAD(s)
141 // has been transferred to |pmd1|.
142 pmd2.reset();
143
144 // Now check that |pmd1| has been effectively merged.
145 ASSERT_EQ(6u, pmd1->allocator_dumps().size());
146 ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad1"));
147 ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
148 ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd2/mad1"));
149 ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
150 ASSERT_EQ(2u, pmd1->allocator_dumps_edges().size());
151 ASSERT_EQ(shared_mad1, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid1));
152 ASSERT_EQ(shared_mad2, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid2));
153 ASSERT_TRUE(MemoryAllocatorDump::Flags::WEAK & shared_mad2->flags());
154 ASSERT_EQ(4u, pmd1->heap_dumps().size());
155 ASSERT_TRUE(GetHeapDump(*pmd1, "pmd1/heap_dump1") != nullptr);
156 ASSERT_TRUE(GetHeapDump(*pmd1, "pmd1/heap_dump2") != nullptr);
157 ASSERT_TRUE(GetHeapDump(*pmd1, "pmd2/heap_dump1") != nullptr);
158 ASSERT_TRUE(GetHeapDump(*pmd1, "pmd2/heap_dump2") != nullptr);
159
160 // Check that calling AsValueInto() doesn't cause a crash.
161 traced_value.reset(new TracedValue);
162 pmd1->AsValueInto(traced_value.get());
163
164 pmd1.reset();
165 }
166
TEST(ProcessMemoryDumpTest,Suballocations)167 TEST(ProcessMemoryDumpTest, Suballocations) {
168 std::unique_ptr<ProcessMemoryDump> pmd(
169 new ProcessMemoryDump(nullptr, kDetailedDumpArgs));
170 const std::string allocator_dump_name = "fakealloc/allocated_objects";
171 pmd->CreateAllocatorDump(allocator_dump_name);
172
173 // Create one allocation with an auto-assigned guid and mark it as a
174 // suballocation of "fakealloc/allocated_objects".
175 auto* pic1_dump = pmd->CreateAllocatorDump("picturemanager/picture1");
176 pmd->AddSuballocation(pic1_dump->guid(), allocator_dump_name);
177
178 // Same here, but this time create an allocation with an explicit guid.
179 auto* pic2_dump = pmd->CreateAllocatorDump("picturemanager/picture2",
180 MemoryAllocatorDumpGuid(0x42));
181 pmd->AddSuballocation(pic2_dump->guid(), allocator_dump_name);
182
183 // Now check that AddSuballocation() has created anonymous child dumps under
184 // "fakealloc/allocated_objects".
185 auto anon_node_1_it = pmd->allocator_dumps().find(
186 allocator_dump_name + "/__" + pic1_dump->guid().ToString());
187 ASSERT_NE(pmd->allocator_dumps().end(), anon_node_1_it);
188
189 auto anon_node_2_it =
190 pmd->allocator_dumps().find(allocator_dump_name + "/__42");
191 ASSERT_NE(pmd->allocator_dumps().end(), anon_node_2_it);
192
193 // Finally check that AddSuballocation() has created also the
194 // edges between the pictures and the anonymous allocator child dumps.
195 bool found_edge[2]{false, false};
196 for (const auto& e : pmd->allocator_dumps_edges()) {
197 found_edge[0] |= (e.source == pic1_dump->guid() &&
198 e.target == anon_node_1_it->second->guid());
199 found_edge[1] |= (e.source == pic2_dump->guid() &&
200 e.target == anon_node_2_it->second->guid());
201 }
202 ASSERT_TRUE(found_edge[0]);
203 ASSERT_TRUE(found_edge[1]);
204
205 // Check that calling AsValueInto() doesn't cause a crash.
206 std::unique_ptr<TracedValue> traced_value(new TracedValue);
207 pmd->AsValueInto(traced_value.get());
208
209 pmd.reset();
210 }
211
TEST(ProcessMemoryDumpTest,GlobalAllocatorDumpTest)212 TEST(ProcessMemoryDumpTest, GlobalAllocatorDumpTest) {
213 std::unique_ptr<ProcessMemoryDump> pmd(
214 new ProcessMemoryDump(nullptr, kDetailedDumpArgs));
215 MemoryAllocatorDumpGuid shared_mad_guid(1);
216 auto* shared_mad1 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
217 ASSERT_EQ(shared_mad_guid, shared_mad1->guid());
218 ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
219
220 auto* shared_mad2 = pmd->GetSharedGlobalAllocatorDump(shared_mad_guid);
221 ASSERT_EQ(shared_mad1, shared_mad2);
222 ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
223
224 auto* shared_mad3 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
225 ASSERT_EQ(shared_mad1, shared_mad3);
226 ASSERT_EQ(MemoryAllocatorDump::Flags::WEAK, shared_mad1->flags());
227
228 auto* shared_mad4 = pmd->CreateSharedGlobalAllocatorDump(shared_mad_guid);
229 ASSERT_EQ(shared_mad1, shared_mad4);
230 ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
231
232 auto* shared_mad5 = pmd->CreateWeakSharedGlobalAllocatorDump(shared_mad_guid);
233 ASSERT_EQ(shared_mad1, shared_mad5);
234 ASSERT_EQ(MemoryAllocatorDump::Flags::DEFAULT, shared_mad1->flags());
235 }
236
TEST(ProcessMemoryDumpTest,BackgroundModeTest)237 TEST(ProcessMemoryDumpTest, BackgroundModeTest) {
238 MemoryDumpArgs background_args = {MemoryDumpLevelOfDetail::BACKGROUND};
239 std::unique_ptr<ProcessMemoryDump> pmd(
240 new ProcessMemoryDump(nullptr, background_args));
241 ProcessMemoryDump::is_black_hole_non_fatal_for_testing_ = true;
242 SetAllocatorDumpNameWhitelistForTesting(kTestDumpNameWhitelist);
243 MemoryAllocatorDump* black_hole_mad = pmd->GetBlackHoleMad();
244
245 // Invalid dump names.
246 EXPECT_EQ(black_hole_mad,
247 pmd->CreateAllocatorDump("NotWhitelisted/TestName"));
248 EXPECT_EQ(black_hole_mad, pmd->CreateAllocatorDump("TestName"));
249 EXPECT_EQ(black_hole_mad, pmd->CreateAllocatorDump("Whitelisted/Test"));
250 EXPECT_EQ(black_hole_mad,
251 pmd->CreateAllocatorDump("Not/Whitelisted/TestName"));
252 EXPECT_EQ(black_hole_mad,
253 pmd->CreateAllocatorDump("Whitelisted/TestName/Google"));
254 EXPECT_EQ(black_hole_mad,
255 pmd->CreateAllocatorDump("Whitelisted/TestName/0x1a2Google"));
256 EXPECT_EQ(black_hole_mad,
257 pmd->CreateAllocatorDump("Whitelisted/TestName/__12/Google"));
258
259 // Global dumps.
260 MemoryAllocatorDumpGuid guid(1);
261 EXPECT_EQ(black_hole_mad, pmd->CreateSharedGlobalAllocatorDump(guid));
262 EXPECT_EQ(black_hole_mad, pmd->CreateWeakSharedGlobalAllocatorDump(guid));
263 EXPECT_EQ(black_hole_mad, pmd->GetSharedGlobalAllocatorDump(guid));
264
265 // Suballocations.
266 pmd->AddSuballocation(guid, "malloc/allocated_objects");
267 EXPECT_EQ(0u, pmd->allocator_dumps_edges_.size());
268 EXPECT_EQ(0u, pmd->allocator_dumps_.size());
269
270 // Valid dump names.
271 EXPECT_NE(black_hole_mad, pmd->CreateAllocatorDump("Whitelisted/TestName"));
272 EXPECT_NE(black_hole_mad,
273 pmd->CreateAllocatorDump("Whitelisted/TestName_0xA1b2"));
274 EXPECT_NE(black_hole_mad,
275 pmd->CreateAllocatorDump("Whitelisted/0xaB/TestName"));
276
277 // GetAllocatorDump is consistent.
278 EXPECT_EQ(black_hole_mad, pmd->GetAllocatorDump("NotWhitelisted/TestName"));
279 EXPECT_NE(black_hole_mad, pmd->GetAllocatorDump("Whitelisted/TestName"));
280 }
281
282 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
TEST(ProcessMemoryDumpTest,CountResidentBytes)283 TEST(ProcessMemoryDumpTest, CountResidentBytes) {
284 const size_t page_size = ProcessMemoryDump::GetSystemPageSize();
285
286 // Allocate few page of dirty memory and check if it is resident.
287 const size_t size1 = 5 * page_size;
288 std::unique_ptr<char, base::AlignedFreeDeleter> memory1(
289 static_cast<char*>(base::AlignedAlloc(size1, page_size)));
290 memset(memory1.get(), 0, size1);
291 size_t res1 = ProcessMemoryDump::CountResidentBytes(memory1.get(), size1);
292 ASSERT_EQ(res1, size1);
293
294 // Allocate a large memory segment (> 8Mib).
295 const size_t kVeryLargeMemorySize = 15 * 1024 * 1024;
296 std::unique_ptr<char, base::AlignedFreeDeleter> memory2(
297 static_cast<char*>(base::AlignedAlloc(kVeryLargeMemorySize, page_size)));
298 memset(memory2.get(), 0, kVeryLargeMemorySize);
299 size_t res2 = ProcessMemoryDump::CountResidentBytes(memory2.get(),
300 kVeryLargeMemorySize);
301 ASSERT_EQ(res2, kVeryLargeMemorySize);
302 }
303 #endif // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
304
305 } // namespace trace_event
306 } // namespace base
307