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
15namespace base {
16namespace trace_event {
17
18TEST(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
66TEST(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
116TEST(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)
161TEST(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