OLD | NEW |
(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 |
OLD | NEW |