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

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

Issue 1892283004: Add cooldown to LeakDetectorImpl leak report generation (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Rebased; remove JuliaSet history comparison Created 4 years, 8 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 side-by-side diff with in-line comments
Download patch
« no previous file with comments | « components/metrics/leak_detector/leak_detector_impl.cc ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: components/metrics/leak_detector/leak_detector_impl_unittest.cc
diff --git a/components/metrics/leak_detector/leak_detector_impl_unittest.cc b/components/metrics/leak_detector/leak_detector_impl_unittest.cc
index 960ac842820a2987040a3015683e3978485ab7cd..5035271fd404b1edfefd458d54e6d5e2ba1a94d6 100644
--- a/components/metrics/leak_detector/leak_detector_impl_unittest.cc
+++ b/components/metrics/leak_detector/leak_detector_impl_unittest.cc
@@ -26,6 +26,9 @@ namespace {
// Makes working with complex numbers easier.
using Complex = std::complex<double>;
+using InternalLeakReport = LeakDetectorImpl::LeakReport;
+using AllocationBreakdown = LeakDetectorImpl::LeakReport::AllocationBreakdown;
+
// The mapping location in memory for a fictional executable.
const uintptr_t kMappingAddr = 0x800000;
const size_t kMappingSize = 0x200000;
@@ -82,6 +85,13 @@ const size_t kAllocedSizeAnalysisInterval = 8192;
const uint32_t kSizeSuspicionThreshold = 4;
const uint32_t kCallStackSuspicionThreshold = 4;
+// Because it takes N+1 analyses to reach a suspicion threshold of N (the
+// suspicion score is only calculated based on deltas from the previous
+// analysis), the actual number of analyses it takes to generate a report for
+// the first time is:
+const uint32_t kMinNumAnalysesToGenerateReport =
+ kSizeSuspicionThreshold + 1 + kCallStackSuspicionThreshold + 1;
+
// Returns the offset within [kMappingAddr, kMappingAddr + kMappingSize) if
// |addr| falls in that range. Otherwise, returns |addr|.
uintptr_t GetOffsetInMapping(uintptr_t addr) {
@@ -96,6 +106,21 @@ uint32_t SizeToIndex(size_t size) {
return size / sizeof(uint32_t);
}
+// Returns true if the |alloc_breakdown_history_| field of the two LeakReports
+// |a| and |b| are the same.
+bool CompareReportAllocHistory(const InternalLeakReport& a,
+ const InternalLeakReport& b) {
+ auto alloc_breakdown_compare_func = [](AllocationBreakdown a,
+ AllocationBreakdown b) -> bool {
+ return std::equal(a.counts_by_size.begin(), a.counts_by_size.end(),
+ b.counts_by_size.begin()) &&
+ a.count_for_call_stack == b.count_for_call_stack;
+ };
+ return std::equal(
+ a.alloc_breakdown_history().begin(), a.alloc_breakdown_history().end(),
+ b.alloc_breakdown_history().begin(), alloc_breakdown_compare_func);
+}
+
} // namespace
// This test suite will test the ability of LeakDetectorImpl to catch leaks in
@@ -110,7 +135,8 @@ class LeakDetectorImplTest : public ::testing::Test {
: total_num_allocs_(0),
total_num_frees_(0),
total_alloced_size_(0),
- next_analysis_total_alloced_size_(kAllocedSizeAnalysisInterval) {}
+ next_analysis_total_alloced_size_(kAllocedSizeAnalysisInterval),
+ num_reports_generated_(0) {}
void SetUp() override {
CustomAllocator::Initialize();
@@ -135,10 +161,8 @@ class LeakDetectorImplTest : public ::testing::Test {
}
protected:
- using InternalLeakReport = LeakDetectorImpl::LeakReport;
template <typename T>
using InternalVector = LeakDetectorImpl::InternalVector<T>;
- using AllocationBreakdown = LeakDetectorImpl::LeakReport::AllocationBreakdown;
// Alloc and free functions that allocate and free heap memory and
// automatically pass alloc/free info to |detector_|. They emulate the
@@ -174,6 +198,7 @@ class LeakDetectorImplTest : public ::testing::Test {
stored_reports_.insert(report);
}
}
+ num_reports_generated_ += reports.size();
// Determine when the next leak analysis should occur.
while (total_alloced_size_ >= next_analysis_total_alloced_size_)
@@ -226,6 +251,10 @@ class LeakDetectorImplTest : public ::testing::Test {
// Store leak reports here. Use a set so duplicate reports are not stored.
std::set<InternalLeakReport> stored_reports_;
+ // Keeps track of the actual number of reports (duplicate or not) that were
+ // generated by |detector_|.
+ size_t num_reports_generated_;
+
private:
DISALLOW_COPY_AND_ASSIGN(LeakDetectorImplTest);
};
@@ -233,7 +262,7 @@ class LeakDetectorImplTest : public ::testing::Test {
void LeakDetectorImplTest::SimpleLeakyFunction(bool enable_leaks) {
std::vector<uint32_t*> ptrs(7);
- const int kNumOuterIterations = 20;
+ const int kNumOuterIterations = 32;
for (int j = 0; j < kNumOuterIterations; ++j) {
// The inner loop allocates 256 bytes. Run it 32 times so that 8192 bytes
// (|kAllocedSizeAnalysisInterval|) are allocated for each iteration of the
@@ -491,7 +520,8 @@ TEST_F(LeakDetectorImplTest, SimpleLeakyFunctionNoLeak) {
// SimpleLeakyFunction() should have run cleanly without leaking.
EXPECT_EQ(total_num_allocs_, total_num_frees_);
EXPECT_EQ(0U, alloced_ptrs_.size());
- ASSERT_EQ(0U, stored_reports_.size());
+ EXPECT_EQ(0U, num_reports_generated_);
+ EXPECT_EQ(0U, stored_reports_.size());
}
TEST_F(LeakDetectorImplTest, SimpleLeakyFunctionWithLeak) {
@@ -500,6 +530,7 @@ TEST_F(LeakDetectorImplTest, SimpleLeakyFunctionWithLeak) {
// SimpleLeakyFunction() should generated some leak reports.
EXPECT_GT(total_num_allocs_, total_num_frees_);
EXPECT_GT(alloced_ptrs_.size(), 0U);
+ EXPECT_EQ(2U, num_reports_generated_);
ASSERT_EQ(2U, stored_reports_.size());
// The reports should be stored in order of size.
@@ -530,11 +561,12 @@ TEST_F(LeakDetectorImplTest, SimpleLeakyFunctionWithLeak) {
// that come right after. So it should count the two extra allocs made at
// call sites |kStack3| and |kStack4|. The formula is |(i + 1) * 32 + 2|,
// where |i| is the iteration index.
- // There should have been one leak analysis per outer loop iteration, for a
- // total of 20 history records (|kNumOuterIterations|) per report.
+ // - It takes |kMinNumAnalysesToGenerateReport| analyses for the first report
+ // to be generated. Subsequent analyises do not generate reports due to the
+ // cooldown mechanism.
const auto& report1_history = report1.alloc_breakdown_history();
- EXPECT_EQ(20U, report1_history.size());
+ EXPECT_EQ(kMinNumAnalysesToGenerateReport, report1_history.size());
for (size_t i = 0; i < report1_history.size(); ++i) {
const AllocationBreakdown& entry = report1_history[i];
@@ -570,15 +602,80 @@ TEST_F(LeakDetectorImplTest, SimpleLeakyFunctionWithLeak) {
// |report2| should have the same size history and call stack history as
// |report1|.
- const auto& report2_history = report2.alloc_breakdown_history();
- auto compare_history_func =
- [](AllocationBreakdown a, AllocationBreakdown b) -> bool {
- return std::equal(a.counts_by_size.begin(), a.counts_by_size.end(),
- b.counts_by_size.begin()) &&
- a.count_for_call_stack == b.count_for_call_stack;
- };
- EXPECT_TRUE(std::equal(report1_history.begin(), report1_history.end(),
- report2_history.begin(), compare_history_func));
+ EXPECT_TRUE(CompareReportAllocHistory(report1, report2));
+}
+
+TEST_F(LeakDetectorImplTest, SimpleLeakyFunctionWithLeakThreeTimes) {
+ // Run three iterations of the leaky function.
+ SimpleLeakyFunction(true /* enable_leaks */);
+ SimpleLeakyFunction(true /* enable_leaks */);
+ SimpleLeakyFunction(true /* enable_leaks */);
+
+ // SimpleLeakyFunction() should have generated three times as many leak
+ // reports, because the number of iterations is the same as the cooldown of
+ // LeakDetectorImpl. But the number of unique reports stored is still two.
+ EXPECT_EQ(6U, num_reports_generated_);
+ ASSERT_EQ(2U, stored_reports_.size());
+
+ // The reports should be stored in order of size.
+
+ // |report1| comes from the call site marked with kStack1, with size=32.
+ const InternalLeakReport& report1 = *stored_reports_.begin();
+ EXPECT_EQ(32U, report1.alloc_size_bytes());
+ ASSERT_EQ(kStack1.depth, report1.call_stack().size());
+ for (size_t i = 0; i < kStack1.depth; ++i) {
+ EXPECT_EQ(GetOffsetInMapping(kStack1.stack[i]), report1.call_stack()[i])
+ << i;
+ }
+
+ // |report2| comes from the call site marked with kStack2, with size=48.
+ const InternalLeakReport& report2 = *(++stored_reports_.begin());
+ EXPECT_EQ(48U, report2.alloc_size_bytes());
+ ASSERT_EQ(kStack2.depth, report2.call_stack().size());
+ for (size_t i = 0; i < kStack2.depth; ++i) {
+ EXPECT_EQ(GetOffsetInMapping(kStack2.stack[i]), report2.call_stack()[i])
+ << i;
+ }
+
+ const auto& report1_history = report1.alloc_breakdown_history();
+ EXPECT_EQ(32U, report1_history.size());
+
+ for (size_t i = 1; i < report1_history.size(); ++i) {
+ const InternalVector<uint32_t>& counts_by_size =
+ report1_history[i].counts_by_size;
+ const InternalVector<uint32_t>& prev_counts_by_size =
+ report1_history[i - 1].counts_by_size;
+ ASSERT_GT(counts_by_size.size(), SizeToIndex(48));
+
+ // Check the two leaky sizes, 32 and 48. At this point, the exact counts
+ // could be computed but the computations are too complex for a unit test.
+ // Instead, check that the counts increase by 32 from the previous count.
+ // Same goes for checking call site counts later.
+ EXPECT_GT(counts_by_size[SizeToIndex(32)], 0U);
+ EXPECT_GT(counts_by_size[SizeToIndex(48)], 0U);
+ EXPECT_EQ(prev_counts_by_size[SizeToIndex(32)] + 32,
+ counts_by_size[SizeToIndex(32)]);
+ EXPECT_EQ(prev_counts_by_size[SizeToIndex(48)] + 32,
+ counts_by_size[SizeToIndex(48)]);
+
+ // Not related to the leaks, but there should be a dangling 16-byte
+ // allocation during each leak analysis, because it hasn't yet been freed.
+ EXPECT_EQ(1U, counts_by_size[SizeToIndex(16)]);
+ }
+
+ // Check call site count over time.
+ ASSERT_LT(kSizeSuspicionThreshold, report1_history.size());
+ // Sufficient time has passed since the first report was generated. The entire
+ // alloc history should contain call site counts.
+ for (size_t i = 1; i < report1_history.size(); ++i) {
+ EXPECT_GT(report1_history[i].count_for_call_stack, 0U);
+ EXPECT_EQ(report1_history[i - 1].count_for_call_stack + 32,
+ report1_history[i].count_for_call_stack);
+ }
+
+ // |report2| should have the same size history and call stack history as
+ // |report1|.
+ EXPECT_TRUE(CompareReportAllocHistory(report1, report2));
}
TEST_F(LeakDetectorImplTest, JuliaSetNoLeak) {
@@ -587,6 +684,7 @@ TEST_F(LeakDetectorImplTest, JuliaSetNoLeak) {
// JuliaSet() should have run cleanly without leaking.
EXPECT_EQ(total_num_allocs_, total_num_frees_);
EXPECT_EQ(0U, alloced_ptrs_.size());
+ EXPECT_EQ(0U, num_reports_generated_);
ASSERT_EQ(0U, stored_reports_.size());
}
@@ -596,6 +694,7 @@ TEST_F(LeakDetectorImplTest, JuliaSetWithLeak) {
// JuliaSet() should have leaked some memory from two call sites.
EXPECT_GT(total_num_allocs_, total_num_frees_);
EXPECT_GT(alloced_ptrs_.size(), 0U);
+ EXPECT_GT(num_reports_generated_, 0U);
// There should be one unique leak report generated for each leaky call site.
ASSERT_EQ(2U, stored_reports_.size());
@@ -644,16 +743,8 @@ TEST_F(LeakDetectorImplTest, JuliaSetWithLeak) {
EXPECT_GT(counts_by_size[size_1_index], counts_by_size[size_0_index] * 10);
EXPECT_GT(counts_by_size[size_2_index], counts_by_size[size_0_index] * 10);
- // |report2| should have the same size history as |report1|, but not the same
- // call stack history.
- const auto& report2_history = report2.alloc_breakdown_history();
- auto compare_size_history_func =
- [](AllocationBreakdown a, AllocationBreakdown b) -> bool {
- return std::equal(a.counts_by_size.begin(), a.counts_by_size.end(),
- b.counts_by_size.begin());
- };
- EXPECT_TRUE(std::equal(report1_history.begin(), report1_history.end(),
- report2_history.begin(), compare_size_history_func));
+ // |report1| and |report2| do not necessarily have the same allocation history
+ // due to the different rates at which they were generated.
Simon Que 2016/04/21 00:33:08 I forgot to remove this in the previous patch set.
}
} // namespace leak_detector
« no previous file with comments | « components/metrics/leak_detector/leak_detector_impl.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698