OLD | NEW |
| (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 "components/metrics/leak_detector/call_stack_table.h" | |
6 | |
7 #include <set> | |
8 | |
9 #include "base/macros.h" | |
10 #include "base/memory/scoped_ptr.h" | |
11 #include "components/metrics/leak_detector/call_stack_manager.h" | |
12 #include "components/metrics/leak_detector/custom_allocator.h" | |
13 #include "testing/gtest/include/gtest/gtest.h" | |
14 | |
15 namespace metrics { | |
16 namespace leak_detector { | |
17 | |
18 namespace { | |
19 | |
20 // Default threshold used for leak analysis. | |
21 const int kDefaultLeakThreshold = 5; | |
22 | |
23 // Some test call stacks. | |
24 const void* kRawStack0[] = { | |
25 reinterpret_cast<const void*>(0xaabbccdd), | |
26 reinterpret_cast<const void*>(0x11223344), | |
27 reinterpret_cast<const void*>(0x55667788), | |
28 reinterpret_cast<const void*>(0x99887766), | |
29 }; | |
30 const void* kRawStack1[] = { | |
31 reinterpret_cast<const void*>(0xdeadbeef), | |
32 reinterpret_cast<const void*>(0x900df00d), | |
33 reinterpret_cast<const void*>(0xcafedeed), | |
34 reinterpret_cast<const void*>(0xdeafbabe), | |
35 }; | |
36 const void* kRawStack2[] = { | |
37 reinterpret_cast<const void*>(0x12345678), | |
38 reinterpret_cast<const void*>(0xabcdef01), | |
39 reinterpret_cast<const void*>(0xfdecab98), | |
40 }; | |
41 const void* kRawStack3[] = { | |
42 reinterpret_cast<const void*>(0xdead0001), | |
43 reinterpret_cast<const void*>(0xbeef0002), | |
44 reinterpret_cast<const void*>(0x900d0003), | |
45 reinterpret_cast<const void*>(0xf00d0004), | |
46 reinterpret_cast<const void*>(0xcafe0005), | |
47 reinterpret_cast<const void*>(0xdeed0006), | |
48 reinterpret_cast<const void*>(0xdeaf0007), | |
49 reinterpret_cast<const void*>(0xbabe0008), | |
50 }; | |
51 | |
52 } // namespace | |
53 | |
54 class CallStackTableTest : public ::testing::Test { | |
55 public: | |
56 CallStackTableTest() | |
57 : stack0_(nullptr), | |
58 stack1_(nullptr), | |
59 stack2_(nullptr), | |
60 stack3_(nullptr) {} | |
61 | |
62 void SetUp() override { | |
63 CustomAllocator::Initialize(); | |
64 | |
65 manager_.reset(new CallStackManager); | |
66 | |
67 // The unit tests expect a certain order to the call stack pointers. It is | |
68 // an important detail when checking the output of LeakAnalyzer's suspected | |
69 // leaks, which are ordered by the leak value (call stack pointer). Use a | |
70 // set to sort the pointers as they are created. | |
71 std::set<const CallStack*> stacks; | |
72 stacks.insert(manager_->GetCallStack(arraysize(kRawStack0), kRawStack0)); | |
73 stacks.insert(manager_->GetCallStack(arraysize(kRawStack1), kRawStack1)); | |
74 stacks.insert(manager_->GetCallStack(arraysize(kRawStack2), kRawStack2)); | |
75 stacks.insert(manager_->GetCallStack(arraysize(kRawStack3), kRawStack3)); | |
76 ASSERT_EQ(4U, stacks.size()); | |
77 | |
78 std::set<const CallStack*>::const_iterator iter = stacks.begin(); | |
79 stack0_ = *iter++; | |
80 stack1_ = *iter++; | |
81 stack2_ = *iter++; | |
82 stack3_ = *iter++; | |
83 } | |
84 | |
85 void TearDown() override { | |
86 // All call stacks generated by |manager_| will be invalidated when it is | |
87 // destroyed. | |
88 stack0_ = nullptr; | |
89 stack1_ = nullptr; | |
90 stack2_ = nullptr; | |
91 stack3_ = nullptr; | |
92 | |
93 // Destroy the call stack manager before shutting down the allocator. | |
94 manager_.reset(); | |
95 | |
96 EXPECT_TRUE(CustomAllocator::Shutdown()); | |
97 } | |
98 | |
99 protected: | |
100 // Unit tests should directly reference these pointers to CallStack objects. | |
101 const CallStack* stack0_; | |
102 const CallStack* stack1_; | |
103 const CallStack* stack2_; | |
104 const CallStack* stack3_; | |
105 | |
106 private: | |
107 scoped_ptr<CallStackManager> manager_; | |
108 | |
109 DISALLOW_COPY_AND_ASSIGN(CallStackTableTest); | |
110 }; | |
111 | |
112 TEST_F(CallStackTableTest, PointerOrder) { | |
113 EXPECT_LT(stack0_, stack1_); | |
114 EXPECT_LT(stack1_, stack2_); | |
115 EXPECT_LT(stack2_, stack3_); | |
116 } | |
117 | |
118 TEST_F(CallStackTableTest, EmptyTable) { | |
119 CallStackTable table(kDefaultLeakThreshold); | |
120 EXPECT_TRUE(table.empty()); | |
121 | |
122 EXPECT_EQ(0U, table.num_allocs()); | |
123 EXPECT_EQ(0U, table.num_frees()); | |
124 | |
125 // The table should be able to gracefully handle an attempt to remove a call | |
126 // stack entry when none exists. | |
127 table.Remove(stack0_); | |
128 table.Remove(stack1_); | |
129 table.Remove(stack2_); | |
130 table.Remove(stack3_); | |
131 | |
132 EXPECT_EQ(0U, table.num_allocs()); | |
133 EXPECT_EQ(0U, table.num_frees()); | |
134 } | |
135 | |
136 TEST_F(CallStackTableTest, InsertionAndRemoval) { | |
137 CallStackTable table(kDefaultLeakThreshold); | |
138 | |
139 table.Add(stack0_); | |
140 EXPECT_EQ(1U, table.size()); | |
141 EXPECT_EQ(1U, table.num_allocs()); | |
142 table.Add(stack1_); | |
143 EXPECT_EQ(2U, table.size()); | |
144 EXPECT_EQ(2U, table.num_allocs()); | |
145 table.Add(stack2_); | |
146 EXPECT_EQ(3U, table.size()); | |
147 EXPECT_EQ(3U, table.num_allocs()); | |
148 table.Add(stack3_); | |
149 EXPECT_EQ(4U, table.size()); | |
150 EXPECT_EQ(4U, table.num_allocs()); | |
151 | |
152 // Add some call stacks that have already been added. There should be no | |
153 // change in the number of entries, as they are aggregated by call stack. | |
154 table.Add(stack2_); | |
155 EXPECT_EQ(4U, table.size()); | |
156 EXPECT_EQ(5U, table.num_allocs()); | |
157 table.Add(stack3_); | |
158 EXPECT_EQ(4U, table.size()); | |
159 EXPECT_EQ(6U, table.num_allocs()); | |
160 | |
161 // Start removing entries. | |
162 EXPECT_EQ(0U, table.num_frees()); | |
163 | |
164 table.Remove(stack0_); | |
165 EXPECT_EQ(3U, table.size()); | |
166 EXPECT_EQ(1U, table.num_frees()); | |
167 table.Remove(stack1_); | |
168 EXPECT_EQ(2U, table.size()); | |
169 EXPECT_EQ(2U, table.num_frees()); | |
170 | |
171 // Removing call stacks with multiple counts will not reduce the overall | |
172 // number of table entries, until the count reaches 0. | |
173 table.Remove(stack2_); | |
174 EXPECT_EQ(2U, table.size()); | |
175 EXPECT_EQ(3U, table.num_frees()); | |
176 table.Remove(stack3_); | |
177 EXPECT_EQ(2U, table.size()); | |
178 EXPECT_EQ(4U, table.num_frees()); | |
179 | |
180 table.Remove(stack2_); | |
181 EXPECT_EQ(1U, table.size()); | |
182 EXPECT_EQ(5U, table.num_frees()); | |
183 table.Remove(stack3_); | |
184 EXPECT_EQ(0U, table.size()); | |
185 EXPECT_EQ(6U, table.num_frees()); | |
186 | |
187 // Now the table should be empty, but attempt to remove some more and make | |
188 // sure nothing breaks. | |
189 table.Remove(stack0_); | |
190 table.Remove(stack1_); | |
191 table.Remove(stack2_); | |
192 table.Remove(stack3_); | |
193 | |
194 EXPECT_TRUE(table.empty()); | |
195 EXPECT_EQ(6U, table.num_allocs()); | |
196 EXPECT_EQ(6U, table.num_frees()); | |
197 } | |
198 | |
199 TEST_F(CallStackTableTest, MassiveInsertionAndRemoval) { | |
200 CallStackTable table(kDefaultLeakThreshold); | |
201 | |
202 for (int i = 0; i < 100; ++i) | |
203 table.Add(stack3_); | |
204 EXPECT_EQ(1U, table.size()); | |
205 EXPECT_EQ(100U, table.num_allocs()); | |
206 | |
207 for (int i = 0; i < 100; ++i) | |
208 table.Add(stack2_); | |
209 EXPECT_EQ(2U, table.size()); | |
210 EXPECT_EQ(200U, table.num_allocs()); | |
211 | |
212 for (int i = 0; i < 100; ++i) | |
213 table.Add(stack1_); | |
214 EXPECT_EQ(3U, table.size()); | |
215 EXPECT_EQ(300U, table.num_allocs()); | |
216 | |
217 for (int i = 0; i < 100; ++i) | |
218 table.Add(stack0_); | |
219 EXPECT_EQ(4U, table.size()); | |
220 EXPECT_EQ(400U, table.num_allocs()); | |
221 | |
222 // Remove them in a different order, by removing one of each stack during one | |
223 // iteration. The size should not decrease until the last iteration. | |
224 EXPECT_EQ(0U, table.num_frees()); | |
225 | |
226 for (int i = 0; i < 100; ++i) { | |
227 table.Remove(stack0_); | |
228 EXPECT_EQ(4U * i + 1, table.num_frees()); | |
229 | |
230 table.Remove(stack1_); | |
231 EXPECT_EQ(4U * i + 2, table.num_frees()); | |
232 | |
233 table.Remove(stack2_); | |
234 EXPECT_EQ(4U * i + 3, table.num_frees()); | |
235 | |
236 table.Remove(stack3_); | |
237 EXPECT_EQ(4U * i + 4, table.num_frees()); | |
238 } | |
239 EXPECT_EQ(400U, table.num_frees()); | |
240 EXPECT_TRUE(table.empty()); | |
241 | |
242 // Try to remove some more from an empty table and make sure nothing breaks. | |
243 table.Remove(stack0_); | |
244 table.Remove(stack1_); | |
245 table.Remove(stack2_); | |
246 table.Remove(stack3_); | |
247 | |
248 EXPECT_TRUE(table.empty()); | |
249 EXPECT_EQ(400U, table.num_allocs()); | |
250 EXPECT_EQ(400U, table.num_frees()); | |
251 } | |
252 | |
253 TEST_F(CallStackTableTest, DetectLeak) { | |
254 CallStackTable table(kDefaultLeakThreshold); | |
255 | |
256 // Add some base number of entries. | |
257 for (int i = 0; i < 60; ++i) | |
258 table.Add(stack0_); | |
259 for (int i = 0; i < 50; ++i) | |
260 table.Add(stack1_); | |
261 for (int i = 0; i < 64; ++i) | |
262 table.Add(stack2_); | |
263 for (int i = 0; i < 72; ++i) | |
264 table.Add(stack3_); | |
265 | |
266 table.TestForLeaks(); | |
267 EXPECT_TRUE(table.leak_analyzer().suspected_leaks().empty()); | |
268 | |
269 // Use the following scheme: | |
270 // - stack0_: increase by 4 each time -- leak suspect | |
271 // - stack1_: increase by 3 each time -- leak suspect | |
272 // - stack2_: increase by 1 each time -- not a suspect | |
273 // - stack3_: alternate between increasing and decreasing - not a suspect | |
274 bool increase_kstack3 = true; | |
275 for (int i = 0; i < kDefaultLeakThreshold; ++i) { | |
276 EXPECT_TRUE(table.leak_analyzer().suspected_leaks().empty()); | |
277 | |
278 for (int j = 0; j < 4; ++j) | |
279 table.Add(stack0_); | |
280 | |
281 for (int j = 0; j < 3; ++j) | |
282 table.Add(stack1_); | |
283 | |
284 table.Add(stack2_); | |
285 | |
286 // Alternate between adding and removing. | |
287 if (increase_kstack3) | |
288 table.Add(stack3_); | |
289 else | |
290 table.Remove(stack3_); | |
291 increase_kstack3 = !increase_kstack3; | |
292 | |
293 table.TestForLeaks(); | |
294 } | |
295 | |
296 // Check that the correct leak values have been detected. | |
297 const auto& leaks = table.leak_analyzer().suspected_leaks(); | |
298 ASSERT_EQ(2U, leaks.size()); | |
299 // Suspected leaks are reported in increasing leak value -- in this case, the | |
300 // CallStack object's address. | |
301 EXPECT_EQ(stack0_, leaks[0].call_stack()); | |
302 EXPECT_EQ(stack1_, leaks[1].call_stack()); | |
303 } | |
304 | |
305 } // namespace leak_detector | |
306 } // namespace metrics | |
OLD | NEW |