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/process/process_metrics.h"
11 #include "base/trace_event/memory_allocator_dump_guid.h"
12 #include "base/trace_event/trace_event_argument.h"
13 #include "testing/gtest/include/gtest/gtest.h"
14
15 namespace base {
16 namespace trace_event {
17
TEST(ProcessMemoryDumpTest,Clear)18 TEST(ProcessMemoryDumpTest, Clear) {
19 scoped_ptr<ProcessMemoryDump> pmd1(new ProcessMemoryDump(nullptr));
20 pmd1->CreateAllocatorDump("mad1");
21 pmd1->CreateAllocatorDump("mad2");
22 ASSERT_FALSE(pmd1->allocator_dumps().empty());
23
24 pmd1->process_totals()->set_resident_set_bytes(42);
25 pmd1->set_has_process_totals();
26
27 pmd1->process_mmaps()->AddVMRegion(ProcessMemoryMaps::VMRegion());
28 pmd1->set_has_process_mmaps();
29
30 pmd1->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
31 MemoryAllocatorDumpGuid(4242));
32
33 MemoryAllocatorDumpGuid shared_mad_guid(1);
34 pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid);
35
36 pmd1->Clear();
37 ASSERT_TRUE(pmd1->allocator_dumps().empty());
38 ASSERT_TRUE(pmd1->allocator_dumps_edges().empty());
39 ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad1"));
40 ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
41 ASSERT_FALSE(pmd1->has_process_totals());
42 ASSERT_FALSE(pmd1->has_process_mmaps());
43 ASSERT_TRUE(pmd1->process_mmaps()->vm_regions().empty());
44 ASSERT_EQ(nullptr, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
45
46 // Check that calling AsValueInto() doesn't cause a crash.
47 scoped_refptr<TracedValue> traced_value(new TracedValue());
48 pmd1->AsValueInto(traced_value.get());
49
50 // Check that the pmd can be reused and behaves as expected.
51 auto mad1 = pmd1->CreateAllocatorDump("mad1");
52 auto mad3 = pmd1->CreateAllocatorDump("mad3");
53 auto shared_mad = pmd1->CreateSharedGlobalAllocatorDump(shared_mad_guid);
54 ASSERT_EQ(3u, pmd1->allocator_dumps().size());
55 ASSERT_EQ(mad1, pmd1->GetAllocatorDump("mad1"));
56 ASSERT_EQ(nullptr, pmd1->GetAllocatorDump("mad2"));
57 ASSERT_EQ(mad3, pmd1->GetAllocatorDump("mad3"));
58 ASSERT_EQ(shared_mad, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
59
60 traced_value = new TracedValue();
61 pmd1->AsValueInto(traced_value.get());
62
63 pmd1.reset();
64 }
65
TEST(ProcessMemoryDumpTest,TakeAllDumpsFrom)66 TEST(ProcessMemoryDumpTest, TakeAllDumpsFrom) {
67 scoped_refptr<TracedValue> traced_value(new TracedValue());
68
69 scoped_ptr<ProcessMemoryDump> pmd1(new ProcessMemoryDump(nullptr));
70 auto mad1_1 = pmd1->CreateAllocatorDump("pmd1/mad1");
71 auto mad1_2 = pmd1->CreateAllocatorDump("pmd1/mad2");
72 pmd1->AddOwnershipEdge(mad1_1->guid(), mad1_2->guid());
73
74 scoped_ptr<ProcessMemoryDump> pmd2(new ProcessMemoryDump(nullptr));
75 auto mad2_1 = pmd2->CreateAllocatorDump("pmd2/mad1");
76 auto mad2_2 = pmd2->CreateAllocatorDump("pmd2/mad2");
77 pmd1->AddOwnershipEdge(mad2_1->guid(), mad2_2->guid());
78
79 MemoryAllocatorDumpGuid shared_mad_guid(1);
80 auto shared_mad = pmd2->CreateSharedGlobalAllocatorDump(shared_mad_guid);
81
82 pmd1->TakeAllDumpsFrom(pmd2.get());
83
84 // Make sure that pmd2 is empty but still usable after it has been emptied.
85 ASSERT_TRUE(pmd2->allocator_dumps().empty());
86 ASSERT_TRUE(pmd2->allocator_dumps_edges().empty());
87 pmd2->CreateAllocatorDump("pmd2/this_mad_stays_with_pmd2");
88 ASSERT_EQ(1u, pmd2->allocator_dumps().size());
89 ASSERT_EQ(1u, pmd2->allocator_dumps().count("pmd2/this_mad_stays_with_pmd2"));
90 pmd2->AddOwnershipEdge(MemoryAllocatorDumpGuid(42),
91 MemoryAllocatorDumpGuid(4242));
92
93 // Check that calling AsValueInto() doesn't cause a crash.
94 pmd2->AsValueInto(traced_value.get());
95
96 // Free the |pmd2| to check that the memory ownership of the two MAD(s)
97 // has been transferred to |pmd1|.
98 pmd2.reset();
99
100 // Now check that |pmd1| has been effectively merged.
101 ASSERT_EQ(5u, pmd1->allocator_dumps().size());
102 ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad1"));
103 ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
104 ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd2/mad1"));
105 ASSERT_EQ(1u, pmd1->allocator_dumps().count("pmd1/mad2"));
106 ASSERT_EQ(2u, pmd1->allocator_dumps_edges().size());
107 ASSERT_EQ(shared_mad, pmd1->GetSharedGlobalAllocatorDump(shared_mad_guid));
108
109 // Check that calling AsValueInto() doesn't cause a crash.
110 traced_value = new TracedValue();
111 pmd1->AsValueInto(traced_value.get());
112
113 pmd1.reset();
114 }
115
TEST(ProcessMemoryDumpTest,Suballocations)116 TEST(ProcessMemoryDumpTest, Suballocations) {
117 scoped_ptr<ProcessMemoryDump> pmd(new ProcessMemoryDump(nullptr));
118 const std::string allocator_dump_name = "fakealloc/allocated_objects";
119 pmd->CreateAllocatorDump(allocator_dump_name);
120
121 // Create one allocation with an auto-assigned guid and mark it as a
122 // suballocation of "fakealloc/allocated_objects".
123 auto pic1_dump = pmd->CreateAllocatorDump("picturemanager/picture1");
124 pmd->AddSuballocation(pic1_dump->guid(), allocator_dump_name);
125
126 // Same here, but this time create an allocation with an explicit guid.
127 auto pic2_dump = pmd->CreateAllocatorDump("picturemanager/picture2",
128 MemoryAllocatorDumpGuid(0x42));
129 pmd->AddSuballocation(pic2_dump->guid(), allocator_dump_name);
130
131 // Now check that AddSuballocation() has created anonymous child dumps under
132 // "fakealloc/allocated_objects".
133 auto anon_node_1_it = pmd->allocator_dumps().find(
134 allocator_dump_name + "/__" + pic1_dump->guid().ToString());
135 ASSERT_NE(pmd->allocator_dumps().end(), anon_node_1_it);
136
137 auto anon_node_2_it =
138 pmd->allocator_dumps().find(allocator_dump_name + "/__42");
139 ASSERT_NE(pmd->allocator_dumps().end(), anon_node_2_it);
140
141 // Finally check that AddSuballocation() has created also the
142 // edges between the pictures and the anonymous allocator child dumps.
143 bool found_edge[2]{false, false};
144 for (const auto& e : pmd->allocator_dumps_edges()) {
145 found_edge[0] |= (e.source == pic1_dump->guid() &&
146 e.target == anon_node_1_it->second->guid());
147 found_edge[1] |= (e.source == pic2_dump->guid() &&
148 e.target == anon_node_2_it->second->guid());
149 }
150 ASSERT_TRUE(found_edge[0]);
151 ASSERT_TRUE(found_edge[1]);
152
153 // Check that calling AsValueInto() doesn't cause a crash.
154 scoped_refptr<TracedValue> traced_value(new TracedValue());
155 pmd->AsValueInto(traced_value.get());
156
157 pmd.reset();
158 }
159
160 #if defined(COUNT_RESIDENT_BYTES_SUPPORTED)
TEST(ProcessMemoryDumpTest,CountResidentBytes)161 TEST(ProcessMemoryDumpTest, CountResidentBytes) {
162 const size_t page_size = base::GetPageSize();
163
164 // Allocate few page of dirty memory and check if it is resident.
165 const size_t size1 = 5 * page_size;
166 scoped_ptr<char, base::AlignedFreeDeleter> memory1(
167 static_cast<char*>(base::AlignedAlloc(size1, page_size)));
168 memset(memory1.get(), 0, size1);
169 size_t res1 = ProcessMemoryDump::CountResidentBytes(memory1.get(), size1);
170 ASSERT_EQ(res1, size1);
171
172 // Allocate a large memory segment (>32Mib).
173 const size_t kVeryLargeMemorySize = 34 * 1024 * 1024;
174 scoped_ptr<char, base::AlignedFreeDeleter> memory2(
175 static_cast<char*>(base::AlignedAlloc(kVeryLargeMemorySize, page_size)));
176 memset(memory2.get(), 0, kVeryLargeMemorySize);
177 size_t res2 = ProcessMemoryDump::CountResidentBytes(memory2.get(),
178 kVeryLargeMemorySize);
179 ASSERT_EQ(res2, kVeryLargeMemorySize);
180 }
181 #endif // defined(COUNT_RESIDENT_BYTES_SUPPORTED)
182
183 } // namespace trace_event
184 } // namespace base
185