Index: components/metrics/leak_detector/leak_detector_unittest.cc |
diff --git a/components/metrics/leak_detector/leak_detector_unittest.cc b/components/metrics/leak_detector/leak_detector_unittest.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c467bceb3bfd0f8586c1c8b7037e725b02302882 |
--- /dev/null |
+++ b/components/metrics/leak_detector/leak_detector_unittest.cc |
@@ -0,0 +1,239 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "components/metrics/leak_detector/leak_detector.h" |
+ |
+#include <set> |
+ |
+#include "base/allocator/allocator_extension.h" |
+#include "base/logging.h" |
+#include "base/macros.h" |
+#include "base/memory/scoped_ptr.h" |
+#include "components/metrics/leak_detector/custom_allocator.h" |
+#include "components/metrics/leak_detector/leak_detector_value_type.h" |
+#include "testing/gtest/include/gtest/gtest.h" |
+ |
+namespace metrics { |
+ |
+namespace { |
+ |
+// Observer class that receives leak reports and stores them in |reports_|. |
+// Only one copy of each unique report will be stored. |
+class TestObserver : public LeakDetector::LeakDetector::Observer { |
+ public: |
+ TestObserver() {} |
+ |
+ void OnLeakFound(const LeakDetector::LeakReport& report) override { |
+ reports_.insert(report); |
+ } |
+ |
+ const std::set<LeakDetector::LeakReport>& reports() const { return reports_; } |
+ |
+ private: |
+ std::set<LeakDetector::LeakReport> reports_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(TestObserver); |
+}; |
+ |
+// Used by CheckStackFrameInfoAlloc() as a return value. |
+bool g_has_stack_frame_info = false; |
+ |
+// Attempt to get a call stack. If the operation returned a positive number of |
+// frames and the first frame is valid, that means the stack frame info is |
+// available. Sets |g_has_stack_frame_info| to indicate whether stack frame info |
+// is available. |
+void CheckStackFrameInfoAlloc(const void* ptr, size_t size) { |
+ void* stack[16]; |
+ stack[0] = nullptr; |
+ int depth = base::allocator::GetCallStack(stack, arraysize(stack), 0); |
+ g_has_stack_frame_info = depth > 0 && stack[0]; |
+} |
+ |
+// Returns true if stack frame info is available. |
+bool HasStackFrameInfo() { |
+ g_has_stack_frame_info = false; |
+ // Register the hook function from the above class. |
+ auto existing_hook = |
+ base::allocator::SetSingleAllocHook(&CheckStackFrameInfoAlloc); |
+ |
+ // This will invoke the hook. |
+ int* dummy = new int; |
+ delete dummy; |
+ |
+ // Restore any existing hook. |
+ base::allocator::SetSingleAllocHook(existing_hook); |
+ |
+ return g_has_stack_frame_info; |
+} |
+ |
+} // namespace |
+ |
+class LeakDetectorTest : public ::testing::Test { |
+ public: |
+ LeakDetectorTest() {} |
+ |
+ void SetUp() override { |
+ // Make sure there are no preexisting hooks registered with the allocator. |
+ ASSERT_EQ(nullptr, base::allocator::GetSingleAllocHook()); |
+ ASSERT_EQ(nullptr, base::allocator::GetSingleFreeHook()); |
+ } |
+ |
+ void TearDown() override { |
+ // Make sure any hooks have been removed after LeakDetector is destroyed |
+ // after the test. |
+ ASSERT_EQ(nullptr, base::allocator::GetSingleAllocHook()); |
+ ASSERT_EQ(nullptr, base::allocator::GetSingleFreeHook()); |
+ } |
+ |
+ private: |
+ DISALLOW_COPY_AND_ASSIGN(LeakDetectorTest); |
+}; |
+ |
+TEST_F(LeakDetectorTest, SingleInstance) { |
+ LeakDetector detector; |
+ |
+ // Make sure some hooks have been set. |
+ EXPECT_NE(nullptr, base::allocator::GetSingleAllocHook()); |
+ EXPECT_NE(nullptr, base::allocator::GetSingleFreeHook()); |
+} |
+ |
+TEST_F(LeakDetectorTest, AddAndRemoveObservers) { |
+ LeakDetector detector; |
+ TestObserver obs1, obs2, obs3; |
+ |
+ // There are no registered observers at first. Make sure RemoveObserver() |
+ // returns false in this case. |
+ EXPECT_FALSE(detector.RemoveObserver(&obs1)); |
+ EXPECT_FALSE(detector.RemoveObserver(&obs2)); |
+ EXPECT_FALSE(detector.RemoveObserver(&obs3)); |
+ |
+ // Add observer and then remove it. |
+ EXPECT_TRUE(detector.AddObserver(&obs1)); |
+ EXPECT_TRUE(detector.RemoveObserver(&obs1)); |
+ EXPECT_FALSE(detector.RemoveObserver(&obs1)); |
+ |
+ // Add all three observers at once, then remove them. |
+ EXPECT_TRUE(detector.AddObserver(&obs1)); |
+ EXPECT_TRUE(detector.AddObserver(&obs2)); |
+ EXPECT_TRUE(detector.AddObserver(&obs3)); |
+ |
+ EXPECT_TRUE(detector.RemoveObserver(&obs1)); |
+ EXPECT_FALSE(detector.RemoveObserver(&obs1)); |
+ EXPECT_TRUE(detector.RemoveObserver(&obs2)); |
+ EXPECT_FALSE(detector.RemoveObserver(&obs2)); |
+ EXPECT_TRUE(detector.RemoveObserver(&obs3)); |
+ EXPECT_FALSE(detector.RemoveObserver(&obs3)); |
+} |
+ |
+TEST_F(LeakDetectorTest, NotifyObservers) { |
+ // Generate two sets of leak reports. |
+ std::vector<LeakDetector::LeakReport> reports1(3); |
+ reports1[0].alloc_size_bytes = 8; |
+ reports1[0].call_stack = {1, 2, 3, 4}; |
+ reports1[1].alloc_size_bytes = 16; |
+ reports1[1].call_stack = {5, 6, 7, 8}; |
+ reports1[2].alloc_size_bytes = 24; |
+ reports1[2].call_stack = {9, 10, 11, 12}; |
+ |
+ std::vector<LeakDetector::LeakReport> reports2(3); |
+ reports2[0].alloc_size_bytes = 32; |
+ reports2[0].call_stack = {1, 2, 4, 8}; |
+ reports2[1].alloc_size_bytes = 40; |
+ reports2[1].call_stack = {16, 32, 64, 128}; |
+ reports2[2].alloc_size_bytes = 48; |
+ reports2[2].call_stack = {256, 512, 1024, 2048}; |
+ |
+ scoped_ptr<LeakDetector> detector(new LeakDetector); |
+ |
+ // Register three observers; |
+ TestObserver obs1, obs2, obs3; |
+ ASSERT_TRUE(detector->AddObserver(&obs1)); |
+ ASSERT_TRUE(detector->AddObserver(&obs2)); |
+ ASSERT_TRUE(detector->AddObserver(&obs3)); |
+ |
+ // Pass both sets of reports to the leak detector. |
+ detector->NotifyObservers(reports1); |
+ detector->NotifyObservers(reports2); |
+ |
+ // Shut down the leak detector before checking the reports, so that the |
+ // stored reports can be examined without new reports being generated. |
+ detector.reset(); |
+ |
+ // Check that all three observers got both sets of reports, passed in |
+ // separately. |
+ for (const TestObserver* obs : {&obs1, &obs2, &obs3}) { |
+ EXPECT_EQ(6U, obs->reports().size()); |
+ for (const auto& report : {reports1[0], reports1[1], reports1[2], |
+ reports2[0], reports2[1], reports2[2]}) { |
+ EXPECT_TRUE(obs->reports().find(report) != obs->reports().end()); |
+ } |
+ } |
+} |
+ |
+TEST_F(LeakDetectorTest, SimpleLeak) { |
+ // The leak detector cannot run properly without call stack frame info. |
+ if (!HasStackFrameInfo()) { |
+ LOG(INFO) << "No stack frame info found, skipping."; |
+ return; |
+ } |
+ |
+ // Initialize with a low analysis interval to quickly find some leaks. The |
+ // value is chosen to be larger than the number of bytes allocated in one |
+ // iteration of the for loop below. |
+ scoped_ptr<LeakDetector> detector( |
+ new LeakDetector(1.0f, // sampling_factor |
+ 8, // max_stack_depth |
+ 1024, // analysis_interval_bytes |
+ 4, // size_suspicion_threshold |
+ 4)); // call_stack_suspicion_threshold |
+ |
+ // Create two observers. |
+ TestObserver obs1, obs2; |
+ ASSERT_TRUE(detector->AddObserver(&obs1)); |
+ ASSERT_TRUE(detector->AddObserver(&obs2)); |
+ |
+ // Store all allocated pointers here so they can be deleted later. |
+ std::vector<scoped_ptr<char[]>> allocated_ptrs; |
+ allocated_ptrs.reserve(4096); |
+ |
+ // The following loop allocates a bunch of memory of various sizes. Sizes 32 |
+ // and 48 are the ones with many more allocations, and from multiple call |
+ // sites locations. They should trigger leaks. |
+ for (int j = 0; j < 100; ++j) { |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[16])); |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[32])); |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[48])); |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[16])); |
+ for (int i = 0; i < 4; ++i) { |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[32])); |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[48])); |
+ } |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[32])); |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[24])); |
+ allocated_ptrs.push_back(scoped_ptr<char[]>(new char[48])); |
+ } |
+ // Shut down the leak detector before checking the reports, so that the |
+ // stored reports can be examined without new reports being generated. |
+ detector.reset(); |
+ |
+ for (const TestObserver* observer : {&obs1, &obs2}) { |
+ // Check that reports were generated for sizes 32 and 48. |
+ const auto& reports = observer->reports(); |
+ ASSERT_EQ(2U, reports.size()); |
+ |
+ auto iter = reports.begin(); |
+ const LeakDetector::LeakReport& report1 = *iter; |
+ EXPECT_EQ(32U, report1.alloc_size_bytes); |
+ |
+ ++iter; |
+ const LeakDetector::LeakReport& report2 = *iter; |
+ EXPECT_EQ(48U, report2.alloc_size_bytes); |
+ |
+ // We don't know what the exact call stacks are, but make sure they are |
+ // distinct. |
+ EXPECT_NE(report1.call_stack, report2.call_stack); |
+ } |
+} |
+ |
+} // namespace metrics |