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

Unified Diff: components/metrics/leak_detector/leak_analyzer_unittest.cc

Issue 986503002: components/metrics: Add runtime memory leak detector (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Addressed Alexei's comments Created 5 years, 1 month 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 side-by-side diff with in-line comments
Download patch
Index: components/metrics/leak_detector/leak_analyzer_unittest.cc
diff --git a/components/metrics/leak_detector/leak_analyzer_unittest.cc b/components/metrics/leak_detector/leak_analyzer_unittest.cc
new file mode 100644
index 0000000000000000000000000000000000000000..27bc5b4d705cb79ec8fbe81686e103984612eb5d
--- /dev/null
+++ b/components/metrics/leak_detector/leak_analyzer_unittest.cc
@@ -0,0 +1,366 @@
+// Copyright 2015 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_analyzer.h"
+
+#include "base/macros.h"
+#include "components/metrics/leak_detector/custom_allocator.h"
+#include "components/metrics/leak_detector/ranked_list.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace metrics {
+namespace leak_detector {
+
+namespace {
+
+// Default ranking size and threshold used for leak analysis.
+const int kDefaultRankedListSize = 10;
+const int kDefaultLeakThreshold = 5;
+
+// Makes it easier to instantiate LeakDetectorValueTypes. Instantiates with an
+// integer value that indicates an allocation size. Storing the size allows us
+// to track the storage of the LeakDetectorValueType object within LeakAnalyzer.
+//
+// There is no need to test this with call stacks in addition to sizes because
+// call stacks will be contained in a LeakDetectorValueType object as well.
+LeakDetectorValueType Size(uint32_t value) {
+ return LeakDetectorValueType(value);
+}
+
+} // namespace
+
+class LeakAnalyzerTest : public ::testing::Test {
+ public:
+ LeakAnalyzerTest() {}
+
+ void SetUp() override {
+ CustomAllocator::Initialize();
+ }
+ void TearDown() override {
+ EXPECT_TRUE(CustomAllocator::Shutdown());
+ }
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(LeakAnalyzerTest);
+};
+
+TEST_F(LeakAnalyzerTest, Empty) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+}
+
+TEST_F(LeakAnalyzerTest, SingleSize) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 10);
+ analyzer.AddSample(list.Pass());
+
+ // No leaks should have been detected.
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ }
+}
+
+TEST_F(LeakAnalyzerTest, VariousSizesWithoutIncrease) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30);
+ list.Add(Size(32), 10);
+ list.Add(Size(56), 90);
+ list.Add(Size(64), 40);
+ analyzer.AddSample(list.Pass());
+
+ // No leaks should have been detected.
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ }
+}
+
+TEST_F(LeakAnalyzerTest, VariousSizesWithEqualIncrease) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30 + i * 10);
+ list.Add(Size(32), 10 + i * 10);
+ list.Add(Size(56), 90 + i * 10);
+ list.Add(Size(64), 40 + i * 10);
+ analyzer.AddSample(list.Pass());
+
+ // No leaks should have been detected.
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ }
+}
+
+TEST_F(LeakAnalyzerTest, NotEnoughRunsToTriggerLeak) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ // Run this one iteration short of the number of cycles needed to trigger a
+ // leak. Note that because LeakAnalyzer requires |kDefaultLeakThreshold|
+ // suspicions based on deltas between AddSample() calls, the below loop needs
+ // to run |kDefaultLeakThreshold + 1| times to trigger a leak.
jar (doing other things) 2015/11/14 02:58:37 nit: Several times you said "...trigger a leak" bu
Simon Que 2015/11/17 00:28:48 Done.
+ for (int i = 0; i <= kDefaultLeakThreshold - 1; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30 + i * 10); // This one has a potential leak.
+ list.Add(Size(32), 10 + i * 2);
+ list.Add(Size(56), 90 + i);
+ list.Add(Size(64), 40 + i / 2);
+ analyzer.AddSample(list.Pass());
+
+ // No leaks should have been detected.
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ }
+}
+
+TEST_F(LeakAnalyzerTest, LeakSingleSize) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ // Run this past the number of iterations required to trigger a leak.
+ for (int i = 0; i < kDefaultLeakThreshold + 10; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(32), 10);
+ list.Add(Size(56), 90);
+ list.Add(Size(24), 30 + i * 10); // This one has a potential leak.
+ list.Add(Size(64), 40);
+ analyzer.AddSample(list.Pass());
+
+ // No leaks should have been detected initially...
+ if (i < kDefaultLeakThreshold) {
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ } else {
+ // ... but there should be reported leaks once the threshold is reached.
+ const auto& leaks = analyzer.suspected_leaks();
+ ASSERT_EQ(1U, leaks.size());
+ EXPECT_EQ(24U, leaks[0].size());
+ }
+ }
+}
+
+TEST_F(LeakAnalyzerTest, LeakSingleSizeOthersAlsoIncreasing) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i < kDefaultLeakThreshold + 10; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30 + i * 10); // This one has a potential leak.
+ list.Add(Size(32), 10 + i * 2);
+ list.Add(Size(56), 90 + i);
+ list.Add(Size(64), 40 + i / 2);
+ analyzer.AddSample(list.Pass());
+
+ // No leaks should have been detected initially...
+ if (i < kDefaultLeakThreshold) {
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ } else {
+ // ... but there should be reported leaks once the threshold is reached.
+ const auto& leaks = analyzer.suspected_leaks();
+ ASSERT_EQ(1U, leaks.size());
+ EXPECT_EQ(24U, leaks[0].size());
+ }
+ }
+}
+
+TEST_F(LeakAnalyzerTest, LeakMultipleSizes) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i < kDefaultLeakThreshold + 10; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30 + i * 5);
+ list.Add(Size(32), 10 + i * 40);
+ list.Add(Size(56), 90 + i * 30);
+ list.Add(Size(64), 40 + i * 20);
+ list.Add(Size(80), 20 + i * 3);
+ analyzer.AddSample(list.Pass());
+
+ // No leaks should have been detected initially...
+ if (i < kDefaultLeakThreshold) {
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ } else {
+ // ... but there should be reported leaks once the threshold is reached.
+ const auto& leaks = analyzer.suspected_leaks();
+ ASSERT_EQ(3U, leaks.size());
+ // These should be in order of increasing allocation size.
+ EXPECT_EQ(32U, leaks[0].size());
+ EXPECT_EQ(56U, leaks[1].size());
+ EXPECT_EQ(64U, leaks[2].size());
+ }
+ }
+}
+
+TEST_F(LeakAnalyzerTest, LeakMultipleSizesValueOrder) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i <= kDefaultLeakThreshold; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ // These are similar to LeakMultipleSizes, but the relative order of
+ // allocation increases is different from the relative order of sizes.
+ list.Add(Size(24), 30 + i * 5);
+ list.Add(Size(32), 10 + i * 20);
+ list.Add(Size(56), 90 + i * 40);
+ list.Add(Size(64), 40 + i * 30);
+ list.Add(Size(80), 20 + i * 3);
+ analyzer.AddSample(list.Pass());
+ }
+
+ const auto& leaks = analyzer.suspected_leaks();
+ ASSERT_EQ(3U, leaks.size());
+ // These should be in order of increasing allocation size, NOT in order of
+ // allocation count or deltas.
+ EXPECT_EQ(32U, leaks[0].size());
+ EXPECT_EQ(56U, leaks[1].size());
+ EXPECT_EQ(64U, leaks[2].size());
+}
+
+TEST_F(LeakAnalyzerTest, EqualIncreasesNoLeak) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30 + i * 10);
+ list.Add(Size(32), 10 + i * 10);
+ list.Add(Size(56), 90 + i * 10);
+ list.Add(Size(64), 40 + i * 10);
+ list.Add(Size(80), 20 + i * 10);
+ analyzer.AddSample(list.Pass());
+
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ }
+}
+
+TEST_F(LeakAnalyzerTest, NotBigEnoughDeltaGap) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ // These all have different increments but there is no clear group of
+ // increases that are larger than the rest.
+ list.Add(Size(24), 30 + i * 80);
+ list.Add(Size(32), 10 + i * 45);
+ list.Add(Size(56), 90 + i * 25);
+ list.Add(Size(64), 40 + i * 15);
+ list.Add(Size(80), 20 + i * 10);
+ analyzer.AddSample(list.Pass());
+
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ }
+}
+
+TEST_F(LeakAnalyzerTest, RepeatedRisesUntilLeakFound) {
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold);
+
+ // Remember, there is an extra iteration beyond |kDefaultLeakThreshold| needed
+ // to actually trigger the leak detection.
+ for (int i = 0; i <= kDefaultLeakThreshold - 2; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30 + i * 10);
+ list.Add(Size(32), 10);
+ list.Add(Size(56), 90);
+ list.Add(Size(64), 40);
+ list.Add(Size(80), 20);
+ analyzer.AddSample(list.Pass());
+
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ }
+
+ // Drop back down to 30.
+ for (int i = 0; i <= kDefaultLeakThreshold - 1; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30 + i * 10);
+ list.Add(Size(32), 10);
+ list.Add(Size(56), 90);
+ list.Add(Size(64), 40);
+ list.Add(Size(80), 20);
+ analyzer.AddSample(list.Pass());
+
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ }
+
+ // Drop back down to 30.
+ for (int i = 0; i <= kDefaultLeakThreshold; ++i) {
+ // Initially there should not be any leak detected.
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+
+ RankedList list(kDefaultRankedListSize);
+ list.Add(Size(24), 30 + i * 10);
+ list.Add(Size(32), 10);
+ list.Add(Size(56), 90);
+ list.Add(Size(64), 40);
+ list.Add(Size(80), 20);
+ analyzer.AddSample(list.Pass());
+ }
+ const auto& leaks = analyzer.suspected_leaks();
+ ASSERT_EQ(1U, leaks.size());
+ EXPECT_EQ(24U, leaks[0].size());
+}
+
+TEST_F(LeakAnalyzerTest, LeakWithMultipleGroupsOfDeltas) {
+ const int kRankedListSize = 20;
+ LeakAnalyzer analyzer(kRankedListSize, kDefaultLeakThreshold);
+
+ for (int i = 0; i <= kDefaultLeakThreshold; ++i) {
+ RankedList list(kRankedListSize);
+ list.Add(Size(24), 30 + i * 10); // A group of smaller deltas.
+ list.Add(Size(32), 10 + i * 3);
+ list.Add(Size(80), 20 + i * 5);
+ list.Add(Size(40), 30 + i * 7);
+ list.Add(Size(56), 90);
+ list.Add(Size(64), 40);
+ list.Add(Size(128), 100);
+ list.Add(Size(44), 100 + i * 10); // A group of medium deltas.
+ list.Add(Size(16), 60 + i * 50);
+ list.Add(Size(4), 20 + i * 40);
+ list.Add(Size(8), 100 + i * 60);
+ list.Add(Size(48), 100);
+ list.Add(Size(72), 60 + i * 240); // A group of largest deltas.
+ list.Add(Size(28), 100);
+ list.Add(Size(100), 100 + i * 200);
+ list.Add(Size(104), 60 + i * 128);
+ analyzer.AddSample(list.Pass());
+ }
+ // Only the group of largest deltas should be caught.
+ const auto& leaks = analyzer.suspected_leaks();
+ ASSERT_EQ(3U, leaks.size());
+ // These should be in order of increasing allocation size.
+ EXPECT_EQ(72U, leaks[0].size());
+ EXPECT_EQ(100U, leaks[1].size());
+ EXPECT_EQ(104U, leaks[2].size());
+}
+
+TEST_F(LeakAnalyzerTest, LeakMultipleSizesWithLargeThreshold) {
+ const int kLeakThreshold = 50;
+ LeakAnalyzer analyzer(kDefaultRankedListSize, kLeakThreshold);
+
+ for (int i = 0; i <= kLeakThreshold + 10; ++i) {
+ RankedList list(kDefaultRankedListSize);
+ // * - Cluster of larger deltas
+ list.Add(Size(24), 30 + i * 5);
+ list.Add(Size(32), 10 + i * 40); // *
jar (doing other things) 2015/11/14 02:58:37 nit: Remove these trailing comments.
Simon Que 2015/11/17 00:28:48 They're designed to clarify to the reader which de
+ list.Add(Size(56), 90 + i * 30); // *
+ list.Add(Size(40), 30 + i * 7);
+ list.Add(Size(64), 40 + i * 25); // *
+ list.Add(Size(80), 20 + i * 3);
+ list.Add(Size(128), 100);
+ list.Add(Size(44), 100 + i * 10);
+ list.Add(Size(16), 60 + i * 50); // *
+ analyzer.AddSample(list.Pass());
+
+ // No leaks should have been detected initially...
+ if (i < kLeakThreshold) {
+ EXPECT_TRUE(analyzer.suspected_leaks().empty());
+ } else {
+ // ... but there should be reported leaks once the threshold is reached.
+ const auto& leaks = analyzer.suspected_leaks();
+ ASSERT_EQ(4U, leaks.size());
+ // These should be in order of increasing allocation size.
+ EXPECT_EQ(16U, leaks[0].size());
+ EXPECT_EQ(32U, leaks[1].size());
+ EXPECT_EQ(56U, leaks[2].size());
+ EXPECT_EQ(64U, leaks[3].size());
+ }
+ }
+}
+
+} // namespace leak_detector
+} // namespace metrics

Powered by Google App Engine
This is Rietveld 408576698