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

Side by Side Diff: components/metrics/leak_detector/leak_detector_unittest.cc

Issue 1665553002: metrics: Connect leak detector to allocator (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Use base::Lock; Create LeakDetector class Created 4 years, 10 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 2016 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/leak_detector.h"
6
7 #include <set>
8
9 #include "base/allocator/allocator_extension.h"
10 #include "base/logging.h"
11 #include "base/macros.h"
12 #include "base/memory/scoped_ptr.h"
13 #include "components/metrics/leak_detector/custom_allocator.h"
14 #include "components/metrics/leak_detector/leak_detector_value_type.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace metrics {
18
19 namespace {
20
21 // Observer class that receives leak reports and stores them in |reports_|.
22 // Only one copy of each unique report will be stored.
23 class TestObserver : public LeakDetector::LeakDetector::Observer {
24 public:
25 TestObserver() {}
26
27 void OnLeakFound(const LeakDetector::LeakReport& report) override {
28 reports_.insert(report);
29 }
30
31 const std::set<LeakDetector::LeakReport>& reports() const { return reports_; }
32
33 private:
34 std::set<LeakDetector::LeakReport> reports_;
35
36 DISALLOW_COPY_AND_ASSIGN(TestObserver);
37 };
38
39 // Dummy allocator hooks, used to test initialization when there are existing
40 // hooks registered.
41 void DummyAllocHook(const void* ptr, size_t size) {}
42 void DummyFreeHook(const void* ptr) {}
43
44 // Used by CheckStackFrameInfoAlloc() as a return value.
45 bool g_has_stack_frame_info = false;
46
47 // Attempt to get a call stack. If the operation returned a positive number of
48 // frames and the first frame is valid, that means the stack frame info is
49 // available. Sets |g_has_stack_frame_info| to indicate whether stack frame info
50 // is available.
51 void CheckStackFrameInfoAlloc(const void* ptr, size_t size) {
52 void* stack[16];
53 stack[0] = nullptr;
54 int depth = base::allocator::GetCallStack(stack, arraysize(stack), 0);
55 g_has_stack_frame_info = depth > 0 && stack[0];
56 }
57
58 // Returns true if stack frame info is available.
59 bool HasStackFrameInfo() {
60 g_has_stack_frame_info = false;
61 // Register the hook function from the above class.
62 auto existing_hook =
63 base::allocator::SetSingleAllocHook(&CheckStackFrameInfoAlloc);
64
65 // This will invoke the hook.
66 int* dummy = new int;
67 delete dummy;
68
69 // Restore any existing hook.
70 base::allocator::SetSingleAllocHook(existing_hook);
71
72 return g_has_stack_frame_info;
73 }
74
75 } // namespace
76
77 class LeakDetectorTest : public ::testing::Test {
78 public:
79 LeakDetectorTest() {}
80
81 void SetUp() override {
82 ASSERT_EQ(nullptr, base::allocator::GetSingleAllocHook());
83 ASSERT_EQ(nullptr, base::allocator::GetSingleFreeHook());
84 }
85
86 void TearDown() override {
87 ASSERT_EQ(nullptr, base::allocator::GetSingleAllocHook());
88 ASSERT_EQ(nullptr, base::allocator::GetSingleFreeHook());
89 }
90
91 private:
92 DISALLOW_COPY_AND_ASSIGN(LeakDetectorTest);
93 };
94
95 TEST_F(LeakDetectorTest, SingleInstance) {
96 scoped_ptr<LeakDetector> detector(new LeakDetector);
97 EXPECT_TRUE(detector->IsInitialized());
98
99 // Make sure some hooks have been set.
100 EXPECT_NE(nullptr, base::allocator::GetSingleAllocHook());
101 EXPECT_NE(nullptr, base::allocator::GetSingleFreeHook());
102
103 // Make sure the hooks have been removed after the instance is destroyed.
104 detector.reset();
105 EXPECT_EQ(nullptr, base::allocator::GetSingleAllocHook());
106 EXPECT_EQ(nullptr, base::allocator::GetSingleFreeHook());
107 }
108
109 TEST_F(LeakDetectorTest, MultipleInstances) {
110 // Create a LeakDetector and get the allocator hook functions that were
111 // registered.
112 scoped_ptr<LeakDetector> detector1(new LeakDetector);
113 EXPECT_TRUE(detector1->IsInitialized());
114 auto alloc_hook = base::allocator::GetSingleAllocHook();
115 auto free_hook = base::allocator::GetSingleFreeHook();
116
117 // Create two more instances and check that the alloc hooks are still the
118 // same.
119 scoped_ptr<LeakDetector> detector2(new LeakDetector);
120 EXPECT_TRUE(detector2->IsInitialized());
121 EXPECT_EQ(alloc_hook, base::allocator::GetSingleAllocHook());
122 EXPECT_EQ(free_hook, base::allocator::GetSingleFreeHook());
123
124 scoped_ptr<LeakDetector> detector3(new LeakDetector);
125 EXPECT_TRUE(detector3->IsInitialized());
126 EXPECT_EQ(alloc_hook, base::allocator::GetSingleAllocHook());
127 EXPECT_EQ(free_hook, base::allocator::GetSingleFreeHook());
128
129 // Now destroy the LeakDetectors in a different order. The hooks should
130 // remain the same until the last LeakDetector is destroyed.
131 detector3.reset();
132 EXPECT_EQ(alloc_hook, base::allocator::GetSingleAllocHook());
133 EXPECT_EQ(free_hook, base::allocator::GetSingleFreeHook());
134
135 detector1.reset();
136 EXPECT_EQ(alloc_hook, base::allocator::GetSingleAllocHook());
137 EXPECT_EQ(free_hook, base::allocator::GetSingleFreeHook());
138
139 detector2.reset();
140 EXPECT_EQ(nullptr, base::allocator::GetSingleAllocHook());
141 EXPECT_EQ(nullptr, base::allocator::GetSingleFreeHook());
142 }
143
144 TEST_F(LeakDetectorTest, MismatchedParameters) {
145 // Create a LeakDetector with a particular set of parameters.
146 scoped_ptr<LeakDetector> detector1(new LeakDetector(1.0f, 1, 2, 3, 4));
147 EXPECT_TRUE(detector1->IsInitialized());
148 auto alloc_hook = base::allocator::GetSingleAllocHook();
149 auto free_hook = base::allocator::GetSingleFreeHook();
150
151 // Create another LeakDetector with different parameters.
152 scoped_ptr<LeakDetector> detector2(new LeakDetector(0.5f, 10, 20, 30, 40));
153 EXPECT_FALSE(detector2->IsInitialized());
154
155 // The hooks should still be valid after an unsuccessful instantation of a
156 // LeakDetector with different parameters.
157 EXPECT_EQ(alloc_hook, base::allocator::GetSingleAllocHook());
158 EXPECT_EQ(free_hook, base::allocator::GetSingleFreeHook());
159
160 // Only after destroying the first instance will the hooks be reset. The
161 // second instance doesn't have to be destroyed.
162 detector1.reset();
163 EXPECT_EQ(nullptr, base::allocator::GetSingleAllocHook());
164 EXPECT_EQ(nullptr, base::allocator::GetSingleFreeHook());
165 }
166
167 TEST_F(LeakDetectorTest, ExistingHooks) {
168 base::allocator::SetSingleAllocHook(&DummyAllocHook);
169 base::allocator::SetSingleFreeHook(&DummyFreeHook);
170 ASSERT_EQ(&DummyAllocHook, base::allocator::GetSingleAllocHook());
171 ASSERT_EQ(&DummyFreeHook, base::allocator::GetSingleFreeHook());
172
173 // Cannot overwrite existing hooks.
174 LeakDetector failed_detector;
175 EXPECT_FALSE(failed_detector.IsInitialized());
176
177 // Remove hooks and try again.
178 base::allocator::SetSingleAllocHook(nullptr);
179 base::allocator::SetSingleFreeHook(nullptr);
180 ASSERT_EQ(nullptr, base::allocator::GetSingleAllocHook());
181 ASSERT_EQ(nullptr, base::allocator::GetSingleFreeHook());
182
183 LeakDetector detector;
184 EXPECT_TRUE(detector.IsInitialized());
185 EXPECT_NE(nullptr, base::allocator::GetSingleAllocHook());
186 EXPECT_NE(nullptr, base::allocator::GetSingleFreeHook());
187 }
188
189 TEST_F(LeakDetectorTest, AddAndRemoveObservers) {
190 LeakDetector detector;
191 TestObserver obs1, obs2, obs3;
192
193 // There are no registered observers at first. Make sure RemoveObserver()
194 // returns false in this case.
195 EXPECT_FALSE(detector.RemoveObserver(&obs1));
196 EXPECT_FALSE(detector.RemoveObserver(&obs2));
197 EXPECT_FALSE(detector.RemoveObserver(&obs3));
198
199 // Add observer and then remove it.
200 EXPECT_TRUE(detector.AddObserver(&obs1));
201 EXPECT_TRUE(detector.RemoveObserver(&obs1));
202 EXPECT_FALSE(detector.RemoveObserver(&obs1));
203
204 // Add all three observers at once, then remove them.
205 EXPECT_TRUE(detector.AddObserver(&obs1));
206 EXPECT_TRUE(detector.AddObserver(&obs2));
207 EXPECT_TRUE(detector.AddObserver(&obs3));
208
209 EXPECT_TRUE(detector.RemoveObserver(&obs1));
210 EXPECT_FALSE(detector.RemoveObserver(&obs1));
211 EXPECT_TRUE(detector.RemoveObserver(&obs2));
212 EXPECT_FALSE(detector.RemoveObserver(&obs2));
213 EXPECT_TRUE(detector.RemoveObserver(&obs3));
214 EXPECT_FALSE(detector.RemoveObserver(&obs3));
215 }
216
217 TEST_F(LeakDetectorTest, NotifyObservers) {
218 // Generate two sets of leak reports.
219 std::vector<LeakDetector::LeakReport> reports1(3);
220 reports1[0].alloc_size_bytes = 8;
221 reports1[0].call_stack = {1, 2, 3, 4};
222 reports1[1].alloc_size_bytes = 16;
223 reports1[1].call_stack = {5, 6, 7, 8};
224 reports1[2].alloc_size_bytes = 24;
225 reports1[2].call_stack = {9, 10, 11, 12};
226
227 std::vector<LeakDetector::LeakReport> reports2(3);
228 reports2[0].alloc_size_bytes = 32;
229 reports2[0].call_stack = {1, 2, 4, 8};
230 reports2[1].alloc_size_bytes = 40;
231 reports2[1].call_stack = {16, 32, 64, 128};
232 reports2[2].alloc_size_bytes = 48;
233 reports2[2].call_stack = {256, 512, 1024, 2048};
234
235 // Create two LeakDetector instances;
236 LeakDetector ld1, ld2;
237 ASSERT_TRUE(ld1.IsInitialized());
238 ASSERT_TRUE(ld2.IsInitialized());
239
240 // Register three observers with each LeakDetector;
241 TestObserver obs1, obs2, obs3;
242 ASSERT_TRUE(ld1.AddObserver(&obs1));
243 ASSERT_TRUE(ld1.AddObserver(&obs2));
244 ASSERT_TRUE(ld1.AddObserver(&obs3));
245 ASSERT_TRUE(ld2.AddObserver(&obs1));
246 ASSERT_TRUE(ld2.AddObserver(&obs2));
247 ASSERT_TRUE(ld2.AddObserver(&obs3));
248
249 // Pass one report to each LeakDetector.
250 ld1.NotifyObservers(reports1);
251 ld2.NotifyObservers(reports2);
252
253 // Check that all three observers got both sets of reports, one from each
254 // LeakDetector.
255 for (const TestObserver* obs : {&obs1, &obs2, &obs3}) {
256 EXPECT_EQ(6U, obs->reports().size());
257 for (const auto& report : {reports1[0], reports1[1], reports1[2],
258 reports2[0], reports2[1], reports2[2]}) {
259 EXPECT_TRUE(obs->reports().find(report) != obs->reports().end());
260 }
261 }
262 }
263
264 TEST_F(LeakDetectorTest, SimpleLeak) {
265 // The leak detector cannot run properly without call stack frame info.
266 if (!HasStackFrameInfo()) {
267 LOG(INFO) << "No stack frame info found, skipping.";
268 return;
269 }
270
271 // Initialize with a low analysis interval to quickly find some leaks. The
272 // value is chosen to be larger than the number of bytes allocated in one
273 // iteration of the for loop below.
274 LeakDetector detector1(1.0f, // sampling_factor
275 8, // max_stack_depth
276 1024, // analysis_interval_bytes
277 4, // size_suspicion_threshold
278 4); // call_stack_suspicion_threshold
279 ASSERT_TRUE(detector1.IsInitialized());
280
281 // Create a second LeakDetector just to make sure it gets the same reports.
282 LeakDetector detector2(1.0f, // sampling_factor
283 8, // max_stack_depth
284 1024, // analysis_interval_bytes
285 4, // size_suspicion_threshold
286 4); // call_stack_suspicion_threshold
287 ASSERT_TRUE(detector2.IsInitialized());
288
289 TestObserver obs1, obs2;
290 ASSERT_TRUE(detector1.AddObserver(&obs1));
291 ASSERT_TRUE(detector2.AddObserver(&obs2));
292
293 // Store all allocated pointers here so they can be deleted later.
294 std::vector<scoped_ptr<char[]>> allocated_ptrs;
295 allocated_ptrs.reserve(4096);
296
297 // The following loop allocates a bunch of memory of various sizes. Sizes 32
298 // and 48 are the ones with many more allocations, and from multiple call
299 // sites locations. They should trigger leaks.
300 for (int j = 0; j < 100; ++j) {
301 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[16]));
302 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[32]));
303 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[48]));
304 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[16]));
305 for (int i = 0; i < 4; ++i) {
306 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[32]));
307 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[48]));
308 }
309 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[32]));
310 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[24]));
311 allocated_ptrs.push_back(scoped_ptr<char[]>(new char[48]));
312 }
313
314 for (const TestObserver* observer : {&obs1, &obs2}) {
315 // Check that reports were generated for sizes 32 and 48.
316 const auto& reports = observer->reports();
317 ASSERT_EQ(2U, reports.size());
318
319 auto iter = reports.begin();
320 const LeakDetector::LeakReport& report1 = *iter;
321 EXPECT_EQ(32U, report1.alloc_size_bytes);
322
323 ++iter;
324 const LeakDetector::LeakReport& report2 = *iter;
325 EXPECT_EQ(48U, report2.alloc_size_bytes);
326
327 // We don't know what the exact call stacks are, but make sure they are
328 // distinct.
329 EXPECT_NE(report1.call_stack, report2.call_stack);
330 }
331 }
332
333 } // namespace metrics
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698