Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(374)

Side by Side Diff: base/trace_event/memory_profiler_allocation_register_unittest.cc

Issue 1371053002: [Tracing] Add allocation register for heap profiling (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@backtrace
Patch Set: Created 5 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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/memory_profiler_allocation_register.h"
6 #include "testing/gtest/include/gtest/gtest.h"
7
8 namespace base {
9 namespace trace_event {
10
11 class AllocationRegisterTest : public testing::Test {
12 public:
13 // Returns the number of cells that the |AllocationlessHashTable| in the
14 // |AllocationRegister| can store per system page.
15 size_t GetNumCellsPerPage() {
16 return GetSystemPageSize() /
17 sizeof(
18 AllocationlessHashTable<AllocationRegister::Allocation>::Cell);
19 }
20
21 uint32_t GetTableSize(const AllocationRegister& reg) {
22 return reg.allocations_.table_size_;
23 }
24
25 uint32_t GetCapacity(const AllocationRegister& reg) {
26 return reg.allocations_.capacity_;
27 }
28
29 uint32_t GetFreshIndex(const AllocationRegister& reg) {
30 return reg.allocations_.fresh_;
31 }
32 };
33
34 // Iterates over all entries in the allocation register and returns the bitwise
35 // or of all addresses stored in it.
36 uintptr_t OrAllAddresses(const AllocationRegister& reg) {
37 uintptr_t acc = 0;
38
39 for (auto i : reg)
40 acc |= reinterpret_cast<uintptr_t>(i.address);
41
42 return acc;
43 }
44
45 // Iterates over all entries in the allocation register and returns the sum of
46 // the sizes of the entries.
47 size_t SumAllSizes(const AllocationRegister& reg) {
48 size_t sum = 0;
49
50 for (auto i : reg)
51 sum += i.size;
52
53 return sum;
54 }
55
56 TEST_F(AllocationRegisterTest, InsertRemoveKeys) {
57 AllocationRegister reg;
58 AllocationContext ctx;
59
60 EXPECT_EQ(0u, OrAllAddresses(reg));
61
62 reg.Insert(reinterpret_cast<void*>(1), 0, ctx);
63
64 EXPECT_EQ(1u, OrAllAddresses(reg));
65
66 reg.Insert(reinterpret_cast<void*>(2), 0, ctx);
67
68 EXPECT_EQ(3u, OrAllAddresses(reg));
69
70 reg.Insert(reinterpret_cast<void*>(4), 0, ctx);
71
72 EXPECT_EQ(7u, OrAllAddresses(reg));
73
74 reg.Remove(reinterpret_cast<void*>(2));
75
76 EXPECT_EQ(5u, OrAllAddresses(reg));
77
78 reg.Remove(reinterpret_cast<void*>(4));
79
80 EXPECT_EQ(1u, OrAllAddresses(reg));
81
82 reg.Remove(reinterpret_cast<void*>(1));
83
84 EXPECT_EQ(0u, OrAllAddresses(reg));
85 }
86
87 TEST_F(AllocationRegisterTest, DoubleFreeIsAllowed) {
88 AllocationRegister reg;
89 AllocationContext ctx;
90
91 reg.Insert(reinterpret_cast<void*>(1), 0, ctx);
92 reg.Insert(reinterpret_cast<void*>(2), 0, ctx);
93 reg.Remove(reinterpret_cast<void*>(1));
94 reg.Remove(reinterpret_cast<void*>(1)); // Remove for the second time.
95 reg.Remove(reinterpret_cast<void*>(4)); // Remove never inserted address.
96
97 EXPECT_EQ(2u, OrAllAddresses(reg));
98 }
99
100 TEST_F(AllocationRegisterTest, DoubleInsertOverwrites) {
101 // TODO(ruuda): Although double insert happens in practice, it should not.
102 // Find out the cause and ban double insert if possible.
103 AllocationRegister reg;
104 AllocationContext ctx;
105 StackFrame frame1 = "Foo";
106 StackFrame frame2 = "Bar";
107
108 ctx.backtrace[0] = frame1;
109 reg.Insert(reinterpret_cast<void*>(1), 11, ctx);
110
111 auto elem = *reg.begin();
112
113 EXPECT_EQ(frame1, elem.context->backtrace[0]);
114 EXPECT_EQ(11u, elem.size);
115 EXPECT_EQ(reinterpret_cast<void*>(1), elem.address);
116
117 ctx.backtrace[0] = frame2;
118 reg.Insert(reinterpret_cast<void*>(1), 13, ctx);
119
120 elem = *reg.begin();
121
122 EXPECT_EQ(frame2, elem.context->backtrace[0]);
123 EXPECT_EQ(13u, elem.size);
124 EXPECT_EQ(reinterpret_cast<void*>(1), elem.address);
125 }
126
127 // Check that even if more entries than the table size are inserted, the
128 // register still behaves correctly.
129 TEST_F(AllocationRegisterTest, InsertRemoveCollisions) {
130 size_t expected_sum = 0;
131 AllocationRegister reg;
132 AllocationContext ctx;
133
134 // By inserting 100 more entries than the table size, there will be at least
135 // 100 collisions.
136 for (uintptr_t i = 1; i <= GetTableSize(reg) + 100; i++) {
137 size_t size = i % 31;
138 expected_sum += size;
139 reg.Insert(reinterpret_cast<void*>(i), size, ctx);
140
141 // Don't check the sum on every iteration to keep the test fast.
142 if (i % (1 << 14) == 0)
143 EXPECT_EQ(expected_sum, SumAllSizes(reg));
144 }
145
146 EXPECT_EQ(expected_sum, SumAllSizes(reg));
147
148 for (uintptr_t i = 1; i <= GetTableSize(reg) + 100; i++) {
149 size_t size = i % 31;
150 expected_sum -= size;
151 reg.Remove(reinterpret_cast<void*>(i));
152
153 if (i % (1 << 14) == 0)
154 EXPECT_EQ(expected_sum, SumAllSizes(reg));
155 }
156
157 EXPECT_EQ(expected_sum, SumAllSizes(reg));
158 }
159
160 // The previous tests are not particularly good for testing iterators, because
161 // elements are removed and inserted in the same order, meaning that the cells
162 // fill up from low to high index, and are then freed from low to high index.
163 // This test removes entries in a different order, to ensure that the iterator
164 // skips over the freed cells properly. Then insert again to ensure that the
165 // free list is utilised properly.
166 TEST_F(AllocationRegisterTest, InsertRemoveRandomOrder) {
167 size_t expected_sum = 0;
168 AllocationRegister reg;
169 AllocationContext ctx;
170
171 uintptr_t generator = 3;
172 uintptr_t prime = 1013;
173 uint32_t initial_fresh = GetFreshIndex(reg);
174
175 for (uintptr_t i = 2; i < prime; i++) {
176 size_t size = i % 31;
177 expected_sum += size;
178 reg.Insert(reinterpret_cast<void*>(i), size, ctx);
179 }
180
181 // This should have used a fresh slot for each of the |prime - 2| inserts.
182 ASSERT_EQ(prime - 2, GetFreshIndex(reg) - initial_fresh);
183
184 // Iterate the numbers 2, 3, ..., prime - 1 in pseudorandom order.
185 for (uintptr_t i = generator; i != 1; i = (i * generator) % prime) {
186 size_t size = i % 31;
187 expected_sum -= size;
188 reg.Remove(reinterpret_cast<void*>(i));
189 EXPECT_EQ(expected_sum, SumAllSizes(reg));
190 }
191
192 ASSERT_EQ(0u, expected_sum);
193
194 // Insert |prime - 2| entries again. This should use cells from the free list,
195 // so the |fresh_| index should not change.
196 for (uintptr_t i = 2; i < prime; i++)
197 reg.Insert(reinterpret_cast<void*>(i), 0, ctx);
198
199 ASSERT_EQ(prime - 2, GetFreshIndex(reg) - initial_fresh);
200
201 // Inserting one more entry should use a fresh cell again.
202 reg.Insert(reinterpret_cast<void*>(prime), 0, ctx);
203 ASSERT_EQ(prime - 1, GetFreshIndex(reg) - initial_fresh);
204 }
205
206 // Check that the process aborts due to hitting the guard page when inserting
207 // too many elements.
208 TEST_F(AllocationRegisterTest, OverflowDeathTest) {
209 AllocationRegister reg;
210 AllocationContext ctx;
211 uintptr_t i;
212
213 // Fill up all of the memory allocated for the register. |GetCapacity(reg)|
214 // minus 1 elements are inserted, because cell 0 is unused, so this should
215 // fill up the available cells exactly.
216 for (i = 1; i < GetCapacity(reg); i++) {
217 reg.Insert(reinterpret_cast<void*>(i), 0, ctx);
218 }
219
220 // Adding just one extra element might still work because the allocated memory
221 // is rounded up to the page size. Adding a page full of elements should cause
222 // overflow.
223 const size_t cells_per_page = GetNumCellsPerPage();
224
225 ASSERT_DEATH(for (size_t j = 0; j < cells_per_page; j++) {
226 reg.Insert(reinterpret_cast<void*>(i + j), 0, ctx);
227 }, "");
228 }
229
230 } // namespace trace_event
231 } // namespace base
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698