diff options
author | sque <sque@chromium.org> | 2016-03-24 08:54:10 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2016-03-24 15:55:05 +0000 |
commit | adc00de0d546fe808b01f2d7e1984e6a52c9e3a4 (patch) | |
tree | 84658b79883923d072ede282904400f5ff34b041 /components | |
parent | 0f90ad8a2751b55f1193112652705d21bbb2a8d0 (diff) | |
download | chromium_src-adc00de0d546fe808b01f2d7e1984e6a52c9e3a4.zip chromium_src-adc00de0d546fe808b01f2d7e1984e6a52c9e3a4.tar.gz chromium_src-adc00de0d546fe808b01f2d7e1984e6a52c9e3a4.tar.bz2 |
metrics: Replace RankedList with RankedSet
Replace the O(n) access of std::list with O(log n) of std::set.
The overall overhead of RankedList is low, as it is only called periodically
from LeakDetectorImpl::TestForLeaks(). However, it has a high overhead
relative to TestForLeaks(). Using a set instead of a list will reduce overhead
during those moments with TestForLeaks() is called.
CPU overhead across entire renderer process, with sampling rate of 1/64:
TestForLeaks RankedSet/List
============ ==============
Using RankedList 0.0107% 0.0501%
Using RankedSet 0.0149% 0.0099%
The overhead of RankedList is 5x that of TestForLeaks(), while RankedSet has
an overhead less than that of TestForLeaks().
BUG=chromium:382705
TEST=components_unittests pass
Review URL: https://codereview.chromium.org/1826023002
Cr-Commit-Position: refs/heads/master@{#383067}
Diffstat (limited to 'components')
-rw-r--r-- | components/components_tests.gyp | 2 | ||||
-rw-r--r-- | components/metrics.gypi | 4 | ||||
-rw-r--r-- | components/metrics/BUILD.gn | 6 | ||||
-rw-r--r-- | components/metrics/leak_detector/call_stack_table.cc | 9 | ||||
-rw-r--r-- | components/metrics/leak_detector/leak_analyzer.cc | 22 | ||||
-rw-r--r-- | components/metrics/leak_detector/leak_analyzer.h | 16 | ||||
-rw-r--r-- | components/metrics/leak_detector/leak_analyzer_unittest.cc | 256 | ||||
-rw-r--r-- | components/metrics/leak_detector/leak_detector_impl.cc | 12 | ||||
-rw-r--r-- | components/metrics/leak_detector/ranked_list.cc | 49 | ||||
-rw-r--r-- | components/metrics/leak_detector/ranked_list_unittest.cc | 271 | ||||
-rw-r--r-- | components/metrics/leak_detector/ranked_set.cc | 52 | ||||
-rw-r--r-- | components/metrics/leak_detector/ranked_set.h (renamed from components/metrics/leak_detector/ranked_list.h) | 52 | ||||
-rw-r--r-- | components/metrics/leak_detector/ranked_set_unittest.cc | 324 |
13 files changed, 570 insertions, 505 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index a0822fc..be9d5dd 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -376,7 +376,7 @@ 'metrics/leak_detector/leak_analyzer_unittest.cc', 'metrics/leak_detector/leak_detector_unittest.cc', 'metrics/leak_detector/leak_detector_impl_unittest.cc', - 'metrics/leak_detector/ranked_list_unittest.cc', + 'metrics/leak_detector/ranked_set_unittest.cc', ], 'mime_util_unittest_sources': [ 'mime_util/mime_util_unittest.cc', diff --git a/components/metrics.gypi b/components/metrics.gypi index 0e82c6b..fe68519 100644 --- a/components/metrics.gypi +++ b/components/metrics.gypi @@ -266,8 +266,8 @@ 'metrics/leak_detector/leak_detector_impl.h', 'metrics/leak_detector/leak_detector_value_type.cc', 'metrics/leak_detector/leak_detector_value_type.h', - 'metrics/leak_detector/ranked_list.cc', - 'metrics/leak_detector/ranked_list.h', + 'metrics/leak_detector/ranked_set.cc', + 'metrics/leak_detector/ranked_set.h', ], }, ], diff --git a/components/metrics/BUILD.gn b/components/metrics/BUILD.gn index ccdf6cb..a6c43f5 100644 --- a/components/metrics/BUILD.gn +++ b/components/metrics/BUILD.gn @@ -134,8 +134,8 @@ if (is_chromeos) { "leak_detector/leak_detector_impl.h", "leak_detector/leak_detector_value_type.cc", "leak_detector/leak_detector_value_type.h", - "leak_detector/ranked_list.cc", - "leak_detector/ranked_list.h", + "leak_detector/ranked_set.cc", + "leak_detector/ranked_set.h", ] deps = [ @@ -294,7 +294,7 @@ if (is_chromeos) { "leak_detector/leak_analyzer_unittest.cc", "leak_detector/leak_detector_impl_unittest.cc", "leak_detector/leak_detector_unittest.cc", - "leak_detector/ranked_list_unittest.cc", + "leak_detector/ranked_set_unittest.cc", ] deps = [ diff --git a/components/metrics/leak_detector/call_stack_table.cc b/components/metrics/leak_detector/call_stack_table.cc index 6121828..56c02bd5 100644 --- a/components/metrics/leak_detector/call_stack_table.cc +++ b/components/metrics/leak_detector/call_stack_table.cc @@ -7,6 +7,7 @@ #include <algorithm> #include "components/metrics/leak_detector/call_stack_manager.h" +#include "components/metrics/leak_detector/ranked_set.h" namespace metrics { namespace leak_detector { @@ -63,17 +64,17 @@ void CallStackTable::Remove(const CallStack* call_stack) { void CallStackTable::TestForLeaks() { // Add all entries to the ranked list. - RankedList ranked_list(kMaxCountOfSuspciousStacks); + RankedSet ranked_entries(kMaxCountOfSuspciousStacks); for (const auto& entry_pair : entry_map_) { const Entry& entry = entry_pair.second; // Assumes that |entry.net_num_allocs| is always > 0. If that changes // elsewhere in this class, this code should be updated to only pass values - // > 0 to |ranked_list|. + // > 0 to |ranked_entries|. LeakDetectorValueType call_stack_value(entry_pair.first); - ranked_list.Add(call_stack_value, entry.net_num_allocs); + ranked_entries.Add(call_stack_value, entry.net_num_allocs); } - leak_analyzer_.AddSample(std::move(ranked_list)); + leak_analyzer_.AddSample(std::move(ranked_entries)); } } // namespace leak_detector diff --git a/components/metrics/leak_detector/leak_analyzer.cc b/components/metrics/leak_detector/leak_analyzer.cc index 773622c..54568ac 100644 --- a/components/metrics/leak_detector/leak_analyzer.cc +++ b/components/metrics/leak_detector/leak_analyzer.cc @@ -11,7 +11,7 @@ namespace leak_detector { namespace { -using RankedEntry = RankedList::Entry; +using RankedEntry = RankedSet::Entry; // Increase suspicion scores by this much each time an entry is suspected as // being a leak. @@ -30,14 +30,14 @@ LeakAnalyzer::LeakAnalyzer(uint32_t ranking_size, LeakAnalyzer::~LeakAnalyzer() {} -void LeakAnalyzer::AddSample(RankedList ranked_list) { +void LeakAnalyzer::AddSample(RankedSet ranked_set) { // Save the ranked entries from the previous call. prev_ranked_entries_ = std::move(ranked_entries_); // Save the current entries. - ranked_entries_ = std::move(ranked_list); + ranked_entries_ = std::move(ranked_set); - RankedList ranked_deltas(ranking_size_); + RankedSet ranked_deltas(ranking_size_); for (const RankedEntry& entry : ranked_entries_) { // Determine what count was recorded for this value last time. uint32_t prev_count = 0; @@ -48,13 +48,13 @@ void LeakAnalyzer::AddSample(RankedList ranked_list) { AnalyzeDeltas(ranked_deltas); } -void LeakAnalyzer::AnalyzeDeltas(const RankedList& ranked_deltas) { +void LeakAnalyzer::AnalyzeDeltas(const RankedSet& ranked_deltas) { bool found_drop = false; - RankedList::const_iterator drop_position = ranked_deltas.end(); + RankedSet::const_iterator drop_position = ranked_deltas.end(); if (ranked_deltas.size() > 1) { - RankedList::const_iterator entry_iter = ranked_deltas.begin(); - RankedList::const_iterator next_entry_iter = ranked_deltas.begin(); + RankedSet::const_iterator entry_iter = ranked_deltas.begin(); + RankedSet::const_iterator next_entry_iter = ranked_deltas.begin(); ++next_entry_iter; // If the first entry is 0, that means all deltas are 0 or negative. Do @@ -80,9 +80,9 @@ void LeakAnalyzer::AnalyzeDeltas(const RankedList& ranked_deltas) { std::set<ValueType, std::less<ValueType>, Allocator<ValueType>> current_suspects; if (found_drop) { - for (RankedList::const_iterator ranked_list_iter = ranked_deltas.begin(); - ranked_list_iter != drop_position; ++ranked_list_iter) { - current_suspects.insert(ranked_list_iter->value); + for (RankedSet::const_iterator ranked_set_iter = ranked_deltas.begin(); + ranked_set_iter != drop_position; ++ranked_set_iter) { + current_suspects.insert(ranked_set_iter->value); } } diff --git a/components/metrics/leak_detector/leak_analyzer.h b/components/metrics/leak_detector/leak_analyzer.h index bf98c6c..55ca297 100644 --- a/components/metrics/leak_detector/leak_analyzer.h +++ b/components/metrics/leak_detector/leak_analyzer.h @@ -13,7 +13,7 @@ #include "base/macros.h" #include "components/metrics/leak_detector/custom_allocator.h" #include "components/metrics/leak_detector/leak_detector_value_type.h" -#include "components/metrics/leak_detector/ranked_list.h" +#include "components/metrics/leak_detector/ranked_set.h" #include "components/metrics/leak_detector/stl_allocator.h" namespace metrics { @@ -33,10 +33,10 @@ class LeakAnalyzer { LeakAnalyzer(uint32_t ranking_size, uint32_t num_suspicions_threshold); ~LeakAnalyzer(); - // Take in a RankedList of allocations, sorted by count. Removes the contents - // of |ranked_list| to be stored internally, which is why it is not passed in - // as a const reference. - void AddSample(RankedList ranked_list); + // Take in a RankedSet of allocations, sorted by count. Removes the contents + // of |ranked_entries| to be stored internally, which is why it is not passed + // in as a const reference. + void AddSample(RankedSet ranked_entries); // Used to report suspected leaks. Reported leaks are sorted by ValueType. const std::vector<ValueType, Allocator<ValueType>>& suspected_leaks() const { @@ -46,7 +46,7 @@ class LeakAnalyzer { private: // Analyze a list of allocation count deltas from the previous iteration. If // anything looks like a possible leak, update the suspicion scores. - void AnalyzeDeltas(const RankedList& ranked_deltas); + void AnalyzeDeltas(const RankedSet& ranked_deltas); // Returns the count for the given value from the previous analysis in // |count|. Returns true if the given value was present in the previous @@ -74,9 +74,9 @@ class LeakAnalyzer { std::vector<ValueType, Allocator<ValueType>> suspected_leaks_; // The most recent allocation entries, since the last call to AddSample(). - RankedList ranked_entries_; + RankedSet ranked_entries_; // The previous allocation entries, from before the last call to AddSample(). - RankedList prev_ranked_entries_; + RankedSet prev_ranked_entries_; DISALLOW_COPY_AND_ASSIGN(LeakAnalyzer); }; diff --git a/components/metrics/leak_detector/leak_analyzer_unittest.cc b/components/metrics/leak_detector/leak_analyzer_unittest.cc index 1f15997..71555b4 100644 --- a/components/metrics/leak_detector/leak_analyzer_unittest.cc +++ b/components/metrics/leak_detector/leak_analyzer_unittest.cc @@ -10,7 +10,7 @@ #include "base/macros.h" #include "components/metrics/leak_detector/custom_allocator.h" -#include "components/metrics/leak_detector/ranked_list.h" +#include "components/metrics/leak_detector/ranked_set.h" #include "testing/gtest/include/gtest/gtest.h" namespace metrics { @@ -19,7 +19,7 @@ namespace leak_detector { namespace { // Default ranking size and threshold used for leak analysis. -const int kDefaultRankedListSize = 10; +const int kDefaultRankedSetSize = 10; const int kDefaultLeakThreshold = 5; // Makes it easier to instantiate LeakDetectorValueTypes. Instantiates with an @@ -46,17 +46,17 @@ class LeakAnalyzerTest : public ::testing::Test { }; TEST_F(LeakAnalyzerTest, Empty) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, kDefaultLeakThreshold); EXPECT_TRUE(analyzer.suspected_leaks().empty()); } TEST_F(LeakAnalyzerTest, SingleSize) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, kDefaultLeakThreshold); for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) { - RankedList list(kDefaultRankedListSize); - list.Add(Size(24), 10); - analyzer.AddSample(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 10); + analyzer.AddSample(std::move(set)); // No leaks should have been detected. EXPECT_TRUE(analyzer.suspected_leaks().empty()); @@ -64,15 +64,15 @@ TEST_F(LeakAnalyzerTest, SingleSize) { } TEST_F(LeakAnalyzerTest, VariousSizesWithoutIncrease) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30); + set.Add(Size(32), 10); + set.Add(Size(56), 90); + set.Add(Size(64), 40); + analyzer.AddSample(std::move(set)); // No leaks should have been detected. EXPECT_TRUE(analyzer.suspected_leaks().empty()); @@ -80,15 +80,15 @@ TEST_F(LeakAnalyzerTest, VariousSizesWithoutIncrease) { } TEST_F(LeakAnalyzerTest, VariousSizesWithEqualIncrease) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30 + i * 10); + set.Add(Size(32), 10 + i * 10); + set.Add(Size(56), 90 + i * 10); + set.Add(Size(64), 40 + i * 10); + analyzer.AddSample(std::move(set)); // No leaks should have been detected. EXPECT_TRUE(analyzer.suspected_leaks().empty()); @@ -96,19 +96,19 @@ TEST_F(LeakAnalyzerTest, VariousSizesWithEqualIncrease) { } TEST_F(LeakAnalyzerTest, NotEnoughRunsToTriggerLeakReport) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, kDefaultLeakThreshold); // Run this one iteration short of the number of cycles needed to trigger a // leak report. Because LeakAnalyzer requires |kDefaultLeakThreshold| // suspicions based on deltas between AddSample() calls, the below loop needs // to run |kDefaultLeakThreshold + 1| times to trigger a leak report. 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30 + i * 10); // This one has a potential leak. + set.Add(Size(32), 10 + i * 2); + set.Add(Size(56), 90 + i); + set.Add(Size(64), 40 + i / 2); + analyzer.AddSample(std::move(set)); // No leaks should have been detected. EXPECT_TRUE(analyzer.suspected_leaks().empty()); @@ -116,16 +116,16 @@ TEST_F(LeakAnalyzerTest, NotEnoughRunsToTriggerLeakReport) { } TEST_F(LeakAnalyzerTest, LeakSingleSize) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, kDefaultLeakThreshold); // Run this past the number of iterations required to trigger a leak report. 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(32), 10); + set.Add(Size(56), 90); + set.Add(Size(24), 30 + i * 10); // This one has a potential leak. + set.Add(Size(64), 40); + analyzer.AddSample(std::move(set)); // No leaks should have been detected initially... if (i < kDefaultLeakThreshold) { @@ -140,15 +140,15 @@ TEST_F(LeakAnalyzerTest, LeakSingleSize) { } TEST_F(LeakAnalyzerTest, LeakSingleSizeOthersAlsoIncreasing) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30 + i * 10); // This one has a potential leak. + set.Add(Size(32), 10 + i * 2); + set.Add(Size(56), 90 + i); + set.Add(Size(64), 40 + i / 2); + analyzer.AddSample(std::move(set)); // No leaks should have been detected initially... if (i < kDefaultLeakThreshold) { @@ -163,16 +163,16 @@ TEST_F(LeakAnalyzerTest, LeakSingleSizeOthersAlsoIncreasing) { } TEST_F(LeakAnalyzerTest, LeakMultipleSizes) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30 + i * 5); + set.Add(Size(32), 10 + i * 40); + set.Add(Size(56), 90 + i * 30); + set.Add(Size(64), 40 + i * 20); + set.Add(Size(80), 20 + i * 3); + analyzer.AddSample(std::move(set)); // No leaks should have been detected initially... if (i < kDefaultLeakThreshold) { @@ -190,18 +190,18 @@ TEST_F(LeakAnalyzerTest, LeakMultipleSizes) { } TEST_F(LeakAnalyzerTest, LeakMultipleSizesValueOrder) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, kDefaultLeakThreshold); for (int i = 0; i <= kDefaultLeakThreshold; ++i) { - RankedList list(kDefaultRankedListSize); + RankedSet set(kDefaultRankedSetSize); // 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(std::move(list)); + set.Add(Size(24), 30 + i * 5); + set.Add(Size(32), 10 + i * 20); + set.Add(Size(56), 90 + i * 40); + set.Add(Size(64), 40 + i * 30); + set.Add(Size(80), 20 + i * 3); + analyzer.AddSample(std::move(set)); } const auto& leaks = analyzer.suspected_leaks(); @@ -214,65 +214,65 @@ TEST_F(LeakAnalyzerTest, LeakMultipleSizesValueOrder) { } TEST_F(LeakAnalyzerTest, EqualIncreasesNoLeak) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30 + i * 10); + set.Add(Size(32), 10 + i * 10); + set.Add(Size(56), 90 + i * 10); + set.Add(Size(64), 40 + i * 10); + set.Add(Size(80), 20 + i * 10); + analyzer.AddSample(std::move(set)); EXPECT_TRUE(analyzer.suspected_leaks().empty()); } } TEST_F(LeakAnalyzerTest, NotBigEnoughDeltaGap) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, kDefaultLeakThreshold); for (int i = 0; i < kDefaultLeakThreshold + 20; ++i) { - RankedList list(kDefaultRankedListSize); + RankedSet set(kDefaultRankedSetSize); // 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(std::move(list)); + set.Add(Size(24), 30 + i * 80); + set.Add(Size(32), 10 + i * 45); + set.Add(Size(56), 90 + i * 25); + set.Add(Size(64), 40 + i * 15); + set.Add(Size(80), 20 + i * 10); + analyzer.AddSample(std::move(set)); EXPECT_TRUE(analyzer.suspected_leaks().empty()); } } TEST_F(LeakAnalyzerTest, RepeatedRisesUntilLeakFound) { - LeakAnalyzer analyzer(kDefaultRankedListSize, kDefaultLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30 + i * 10); + set.Add(Size(32), 10); + set.Add(Size(56), 90); + set.Add(Size(64), 40); + set.Add(Size(80), 20); + analyzer.AddSample(std::move(set)); 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30 + i * 10); + set.Add(Size(32), 10); + set.Add(Size(56), 90); + set.Add(Size(64), 40); + set.Add(Size(80), 20); + analyzer.AddSample(std::move(set)); EXPECT_TRUE(analyzer.suspected_leaks().empty()); } @@ -282,13 +282,13 @@ TEST_F(LeakAnalyzerTest, RepeatedRisesUntilLeakFound) { // 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(std::move(list)); + RankedSet set(kDefaultRankedSetSize); + set.Add(Size(24), 30 + i * 10); + set.Add(Size(32), 10); + set.Add(Size(56), 90); + set.Add(Size(64), 40); + set.Add(Size(80), 20); + analyzer.AddSample(std::move(set)); } const auto& leaks = analyzer.suspected_leaks(); ASSERT_EQ(1U, leaks.size()); @@ -296,28 +296,28 @@ TEST_F(LeakAnalyzerTest, RepeatedRisesUntilLeakFound) { } TEST_F(LeakAnalyzerTest, LeakWithMultipleGroupsOfDeltas) { - const int kRankedListSize = 20; - LeakAnalyzer analyzer(kRankedListSize, kDefaultLeakThreshold); + const int kRankedSetSize = 20; + LeakAnalyzer analyzer(kRankedSetSize, 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(std::move(list)); + RankedSet set(kRankedSetSize); + set.Add(Size(24), 30 + i * 10); // A group of smaller deltas. + set.Add(Size(32), 10 + i * 3); + set.Add(Size(80), 20 + i * 5); + set.Add(Size(40), 30 + i * 7); + set.Add(Size(56), 90); + set.Add(Size(64), 40); + set.Add(Size(128), 100); + set.Add(Size(44), 100 + i * 10); // A group of medium deltas. + set.Add(Size(16), 60 + i * 50); + set.Add(Size(4), 20 + i * 40); + set.Add(Size(8), 100 + i * 60); + set.Add(Size(48), 100); + set.Add(Size(72), 60 + i * 240); // A group of largest deltas. + set.Add(Size(28), 100); + set.Add(Size(100), 100 + i * 200); + set.Add(Size(104), 60 + i * 128); + analyzer.AddSample(std::move(set)); } // Only the group of largest deltas should be caught. const auto& leaks = analyzer.suspected_leaks(); @@ -330,21 +330,21 @@ TEST_F(LeakAnalyzerTest, LeakWithMultipleGroupsOfDeltas) { TEST_F(LeakAnalyzerTest, LeakMultipleSizesWithLargeThreshold) { const int kLeakThreshold = 50; - LeakAnalyzer analyzer(kDefaultRankedListSize, kLeakThreshold); + LeakAnalyzer analyzer(kDefaultRankedSetSize, kLeakThreshold); for (int i = 0; i <= kLeakThreshold + 10; ++i) { - RankedList list(kDefaultRankedListSize); + RankedSet set(kDefaultRankedSetSize); // * - Cluster of larger deltas - list.Add(Size(24), 30 + i * 5); - list.Add(Size(32), 10 + i * 40); // * - 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(std::move(list)); + set.Add(Size(24), 30 + i * 5); + set.Add(Size(32), 10 + i * 40); // * + set.Add(Size(56), 90 + i * 30); // * + set.Add(Size(40), 30 + i * 7); + set.Add(Size(64), 40 + i * 25); // * + set.Add(Size(80), 20 + i * 3); + set.Add(Size(128), 100); + set.Add(Size(44), 100 + i * 10); + set.Add(Size(16), 60 + i * 50); // * + analyzer.AddSample(std::move(set)); // No leaks should have been detected initially... if (i < kLeakThreshold) { diff --git a/components/metrics/leak_detector/leak_detector_impl.cc b/components/metrics/leak_detector/leak_detector_impl.cc index 99988d2..8aa3b4e 100644 --- a/components/metrics/leak_detector/leak_detector_impl.cc +++ b/components/metrics/leak_detector/leak_detector_impl.cc @@ -14,7 +14,7 @@ #include "base/process/process_handle.h" #include "components/metrics/leak_detector/call_stack_table.h" #include "components/metrics/leak_detector/custom_allocator.h" -#include "components/metrics/leak_detector/ranked_list.h" +#include "components/metrics/leak_detector/ranked_set.h" namespace metrics { namespace leak_detector { @@ -22,7 +22,7 @@ namespace leak_detector { namespace { // Look for leaks in the the top N entries in each tier, where N is this value. -const int kRankedListSize = 16; +const int kRankedSetSize = 16; // Initial hash table size for |LeakDetectorImpl::address_map_|. const int kAddressMapNumBuckets = 100003; @@ -76,7 +76,7 @@ LeakDetectorImpl::LeakDetectorImpl(uintptr_t mapping_addr, num_allocs_with_call_stack_(0), num_stack_tables_(0), address_map_(kAddressMapNumBuckets), - size_leak_analyzer_(kRankedListSize, size_suspicion_threshold), + size_leak_analyzer_(kRankedSetSize, size_suspicion_threshold), size_entries_(kNumSizeEntries), mapping_addr_(mapping_addr), mapping_size_(mapping_size), @@ -149,13 +149,13 @@ void LeakDetectorImpl::RecordFree(const void* ptr) { void LeakDetectorImpl::TestForLeaks(InternalVector<LeakReport>* reports) { // Add net alloc counts for each size to a ranked list. - RankedList size_ranked_list(kRankedListSize); + RankedSet size_ranked_set(kRankedSetSize); for (size_t i = 0; i < size_entries_.size(); ++i) { const AllocSizeEntry& entry = size_entries_[i]; ValueType size_value(IndexToSize(i)); - size_ranked_list.Add(size_value, entry.num_allocs - entry.num_frees); + size_ranked_set.Add(size_value, entry.num_allocs - entry.num_frees); } - size_leak_analyzer_.AddSample(std::move(size_ranked_list)); + size_leak_analyzer_.AddSample(std::move(size_ranked_set)); // Get suspected leaks by size. for (const ValueType& size_value : size_leak_analyzer_.suspected_leaks()) { diff --git a/components/metrics/leak_detector/ranked_list.cc b/components/metrics/leak_detector/ranked_list.cc deleted file mode 100644 index d2f335d..0000000 --- a/components/metrics/leak_detector/ranked_list.cc +++ /dev/null @@ -1,49 +0,0 @@ -// 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/ranked_list.h" - -#include <algorithm> - -namespace metrics { -namespace leak_detector { - -RankedList::RankedList(size_t max_size) : max_size_(max_size) {} - -RankedList::~RankedList() {} - -RankedList::RankedList(RankedList&& other) - : max_size_(other.max_size_) { - entries_ = std::move(other.entries_); -} - -RankedList& RankedList::operator=(RankedList&& other) { - max_size_ = other.max_size_; - entries_ = std::move(other.entries_); - return *this; -} - -void RankedList::Add(const ValueType& value, int count) { - Entry new_entry; - new_entry.value = value; - new_entry.count = count; - - // Determine where to insert the value given its count. - EntryList::iterator iter = std::upper_bound(entries_.begin(), entries_.end(), - new_entry); - - // If the list is full, do not add any entry with |count| if does not exceed - // the lowest count of the entries in the list. - if (size() == max_size_ && iter == end()) - return; - - entries_.insert(iter, new_entry); - - // Limit the list size if it exceeds the maximum allowed size. - if (entries_.size() > max_size_) - entries_.resize(max_size_); -} - -} // namespace leak_detector -} // namespace metrics diff --git a/components/metrics/leak_detector/ranked_list_unittest.cc b/components/metrics/leak_detector/ranked_list_unittest.cc deleted file mode 100644 index 43e462d..0000000 --- a/components/metrics/leak_detector/ranked_list_unittest.cc +++ /dev/null @@ -1,271 +0,0 @@ -// 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/ranked_list.h" - -#include <stddef.h> -#include <stdint.h> - -#include <algorithm> - -#include "base/macros.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 leak_detector { - -namespace { - -// Makes it easier to instantiate LeakDetectorValueTypes. -LeakDetectorValueType Value(uint32_t value) { - return LeakDetectorValueType(value); -} - -} // namespace - -class RankedListTest : public ::testing::Test { - public: - RankedListTest() {} - - void SetUp() override { CustomAllocator::Initialize(); } - void TearDown() override { EXPECT_TRUE(CustomAllocator::Shutdown()); } - - private: - DISALLOW_COPY_AND_ASSIGN(RankedListTest); -}; - -TEST_F(RankedListTest, Iterators) { - RankedList list(10); - EXPECT_TRUE(list.begin() == list.end()); - - list.Add(Value(0x1234), 100); - EXPECT_FALSE(list.begin() == list.end()); -} - -TEST_F(RankedListTest, SingleInsertion) { - RankedList list(10); - EXPECT_EQ(0U, list.size()); - - list.Add(Value(0x1234), 100); - EXPECT_EQ(1U, list.size()); - - auto iter = list.begin(); - EXPECT_EQ(0x1234U, iter->value.size()); - EXPECT_EQ(100, iter->count); -} - -TEST_F(RankedListTest, InOrderInsertion) { - RankedList list(10); - EXPECT_EQ(0U, list.size()); - - list.Add(Value(0x1234), 100); - EXPECT_EQ(1U, list.size()); - list.Add(Value(0x2345), 95); - EXPECT_EQ(2U, list.size()); - list.Add(Value(0x3456), 90); - EXPECT_EQ(3U, list.size()); - list.Add(Value(0x4567), 85); - EXPECT_EQ(4U, list.size()); - list.Add(Value(0x5678), 80); - EXPECT_EQ(5U, list.size()); - - // Iterate through the contents to make sure they match what went in. - const RankedList::Entry kExpectedValues[] = { - {Value(0x1234), 100}, {Value(0x2345), 95}, {Value(0x3456), 90}, - {Value(0x4567), 85}, {Value(0x5678), 80}, - }; - - size_t index = 0; - for (const auto& entry : list) { - EXPECT_LT(index, arraysize(kExpectedValues)); - EXPECT_EQ(kExpectedValues[index].value.size(), entry.value.size()); - EXPECT_EQ(kExpectedValues[index].count, entry.count); - ++index; - } -} - -TEST_F(RankedListTest, ReverseOrderInsertion) { - RankedList list(10); - EXPECT_EQ(0U, list.size()); - - list.Add(Value(0x1234), 0); - EXPECT_EQ(1U, list.size()); - list.Add(Value(0x2345), 5); - EXPECT_EQ(2U, list.size()); - list.Add(Value(0x3456), 10); - EXPECT_EQ(3U, list.size()); - list.Add(Value(0x4567), 15); - EXPECT_EQ(4U, list.size()); - list.Add(Value(0x5678), 20); - EXPECT_EQ(5U, list.size()); - - // Iterate through the contents to make sure they match what went in. - const RankedList::Entry kExpectedValues[] = { - {Value(0x5678), 20}, {Value(0x4567), 15}, {Value(0x3456), 10}, - {Value(0x2345), 5}, {Value(0x1234), 0}, - }; - - size_t index = 0; - for (const auto& entry : list) { - EXPECT_LT(index, arraysize(kExpectedValues)); - EXPECT_EQ(kExpectedValues[index].value.size(), entry.value.size()); - EXPECT_EQ(kExpectedValues[index].count, entry.count); - ++index; - } -} - -TEST_F(RankedListTest, UnorderedInsertion) { - RankedList list(10); - EXPECT_EQ(0U, list.size()); - - list.Add(Value(0x1234), 15); - list.Add(Value(0x2345), 20); - list.Add(Value(0x3456), 10); - list.Add(Value(0x4567), 30); - list.Add(Value(0x5678), 25); - EXPECT_EQ(5U, list.size()); - - // Iterate through the contents to make sure they match what went in. - const RankedList::Entry kExpectedValues1[] = { - {Value(0x4567), 30}, {Value(0x5678), 25}, {Value(0x2345), 20}, - {Value(0x1234), 15}, {Value(0x3456), 10}, - }; - - size_t index = 0; - for (const auto& entry : list) { - EXPECT_LT(index, arraysize(kExpectedValues1)); - EXPECT_EQ(kExpectedValues1[index].value.size(), entry.value.size()); - EXPECT_EQ(kExpectedValues1[index].count, entry.count); - ++index; - } - - // Add more items. - list.Add(Value(0x6789), 35); - list.Add(Value(0x789a), 40); - list.Add(Value(0x89ab), 50); - list.Add(Value(0x9abc), 5); - list.Add(Value(0xabcd), 0); - EXPECT_EQ(10U, list.size()); - - // Iterate through the contents to make sure they match what went in. - const RankedList::Entry kExpectedValues2[] = { - {Value(0x89ab), 50}, {Value(0x789a), 40}, {Value(0x6789), 35}, - {Value(0x4567), 30}, {Value(0x5678), 25}, {Value(0x2345), 20}, - {Value(0x1234), 15}, {Value(0x3456), 10}, {Value(0x9abc), 5}, - {Value(0xabcd), 0}, - }; - - index = 0; - for (const auto& entry : list) { - EXPECT_LT(index, arraysize(kExpectedValues2)); - EXPECT_EQ(kExpectedValues2[index].value.size(), entry.value.size()); - EXPECT_EQ(kExpectedValues2[index].count, entry.count); - ++index; - } -} - -TEST_F(RankedListTest, InsertionWithOverflow) { - RankedList list(5); - EXPECT_EQ(0U, list.size()); - - list.Add(Value(0x1234), 15); - list.Add(Value(0x2345), 20); - list.Add(Value(0x3456), 10); - list.Add(Value(0x4567), 30); - list.Add(Value(0x5678), 25); - EXPECT_EQ(5U, list.size()); - - // These values will not make it into the list, which is now full. - list.Add(Value(0x6789), 0); - EXPECT_EQ(5U, list.size()); - list.Add(Value(0x789a), 5); - EXPECT_EQ(5U, list.size()); - - // Iterate through the contents to make sure they match what went in. - const RankedList::Entry kExpectedValues1[] = { - {Value(0x4567), 30}, {Value(0x5678), 25}, {Value(0x2345), 20}, - {Value(0x1234), 15}, {Value(0x3456), 10}, - }; - - size_t index = 0; - for (const auto& entry : list) { - EXPECT_LT(index, arraysize(kExpectedValues1)); - EXPECT_EQ(kExpectedValues1[index].value.size(), entry.value.size()); - EXPECT_EQ(kExpectedValues1[index].count, entry.count); - ++index; - } - - // Insert some more values that go in the middle of the list. - list.Add(Value(0x89ab), 27); - EXPECT_EQ(5U, list.size()); - list.Add(Value(0x9abc), 22); - EXPECT_EQ(5U, list.size()); - - // Iterate through the contents to make sure they match what went in. - const RankedList::Entry kExpectedValues2[] = { - {Value(0x4567), 30}, {Value(0x89ab), 27}, {Value(0x5678), 25}, - {Value(0x9abc), 22}, {Value(0x2345), 20}, - }; - - index = 0; - for (const auto& entry : list) { - EXPECT_LT(index, arraysize(kExpectedValues2)); - EXPECT_EQ(kExpectedValues2[index].value.size(), entry.value.size()); - EXPECT_EQ(kExpectedValues2[index].count, entry.count); - ++index; - } - - // Insert some more values at the front of the list. - list.Add(Value(0xabcd), 40); - EXPECT_EQ(5U, list.size()); - list.Add(Value(0xbcde), 35); - EXPECT_EQ(5U, list.size()); - - // Iterate through the contents to make sure they match what went in. - const RankedList::Entry kExpectedValues3[] = { - {Value(0xabcd), 40}, {Value(0xbcde), 35}, {Value(0x4567), 30}, - {Value(0x89ab), 27}, {Value(0x5678), 25}, - }; - - index = 0; - for (const auto& entry : list) { - EXPECT_LT(index, arraysize(kExpectedValues3)); - EXPECT_EQ(kExpectedValues3[index].value.size(), entry.value.size()); - EXPECT_EQ(kExpectedValues3[index].count, entry.count); - ++index; - } -} - -TEST_F(RankedListTest, MoveOperation) { - const RankedList::Entry kExpectedValues[] = { - {Value(0x89ab), 50}, {Value(0x789a), 40}, {Value(0x6789), 35}, - {Value(0x4567), 30}, {Value(0x5678), 25}, {Value(0x2345), 20}, - {Value(0x1234), 15}, {Value(0x3456), 10}, {Value(0x9abc), 5}, - {Value(0xabcd), 0}, - }; - - RankedList source_list(10); - for (const RankedList::Entry& entry : kExpectedValues) { - source_list.Add(entry.value, entry.count); - } - EXPECT_EQ(10U, source_list.size()); - - RankedList dest_list(25); // This should be changed by the move. - dest_list = std::move(source_list); - EXPECT_EQ(10U, dest_list.size()); - EXPECT_EQ(10U, dest_list.max_size()); - - size_t index = 0; - for (const auto& entry : dest_list) { - EXPECT_LT(index, arraysize(kExpectedValues)); - EXPECT_EQ(kExpectedValues[index].value.size(), entry.value.size()); - EXPECT_EQ(kExpectedValues[index].count, entry.count); - ++index; - } -} - -} // namespace leak_detector -} // namespace metrics diff --git a/components/metrics/leak_detector/ranked_set.cc b/components/metrics/leak_detector/ranked_set.cc new file mode 100644 index 0000000..5725fb5 --- /dev/null +++ b/components/metrics/leak_detector/ranked_set.cc @@ -0,0 +1,52 @@ +// 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/ranked_set.h" + +#include <algorithm> + +namespace metrics { +namespace leak_detector { + +RankedSet::RankedSet(size_t max_size) : max_size_(max_size) {} + +RankedSet::~RankedSet() {} + +RankedSet::RankedSet(RankedSet&& other) : max_size_(other.max_size_) { + entries_ = std::move(other.entries_); +} + +RankedSet& RankedSet::operator=(RankedSet&& other) { + max_size_ = other.max_size_; + entries_ = std::move(other.entries_); + return *this; +} + +bool RankedSet::Entry::operator<(const RankedSet::Entry& other) const { + if (count == other.count) + return value < other.value; + + return count > other.count; +} + +void RankedSet::Add(const ValueType& value, int count) { + // If the container is full, do not add any entry with |count| if does not + // exceed the lowest count of the entries in the list. + if (size() == max_size_ && count < min_count()) + return; + + Entry new_entry; + new_entry.value = value; + new_entry.count = count; + entries_.insert(new_entry); + + // Limit the container size if it exceeds the maximum allowed size, by + // deleting the last element. This should only iterate once because the size + // can only have increased by 1, but use a while loop just to be safe. + while (entries_.size() > max_size_) + entries_.erase(--entries_.end()); +} + +} // namespace leak_detector +} // namespace metrics diff --git a/components/metrics/leak_detector/ranked_list.h b/components/metrics/leak_detector/ranked_set.h index 3058ca1..92c751c 100644 --- a/components/metrics/leak_detector/ranked_list.h +++ b/components/metrics/leak_detector/ranked_set.h @@ -2,12 +2,13 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#ifndef COMPONENTS_METRICS_LEAK_DETECTOR_RANKED_LIST_H_ -#define COMPONENTS_METRICS_LEAK_DETECTOR_RANKED_LIST_H_ +#ifndef COMPONENTS_METRICS_LEAK_DETECTOR_RANKED_SET_H_ +#define COMPONENTS_METRICS_LEAK_DETECTOR_RANKED_SET_H_ #include <stddef.h> -#include <list> +#include <functional> // for std::less +#include <set> #include "base/macros.h" #include "components/metrics/leak_detector/custom_allocator.h" @@ -17,35 +18,38 @@ namespace metrics { namespace leak_detector { -// RankedList lets you add entries consisting of a value-count pair, and +// RankedSet lets you add entries consisting of a value-count pair, and // automatically sorts them internally by count in descending order. This allows -// for the user of this list to put value-count pairs into this list without -// having to explicitly sort them by count. -class RankedList { +// for the user of this container to insert value-count pairs without having to +// explicitly sort them by count. +class RankedSet { public: using ValueType = LeakDetectorValueType; - // A single entry in the RankedList. The RankedList sorts entries by |count| + // A single entry in the RankedSet. The RankedSet sorts entries by |count| // in descending order. struct Entry { ValueType value; int count; - // Create a < comparator for reverse sorting. - bool operator<(const Entry& entry) const { return count > entry.count; } + // This less-than comparator is used for sorting Entries within a sorted + // container. It internally reverses the comparison so that higher-count + // entries are sorted ahead of lower-count entries. + bool operator<(const Entry& other) const; }; // This class uses CustomAllocator to avoid recursive malloc hook invocation // when analyzing allocs and frees. - using EntryList = std::list<Entry, STLAllocator<Entry, CustomAllocator>>; - using const_iterator = EntryList::const_iterator; + using EntrySet = + std::set<Entry, std::less<Entry>, STLAllocator<Entry, CustomAllocator>>; + using const_iterator = EntrySet::const_iterator; - explicit RankedList(size_t max_size); - ~RankedList(); + explicit RankedSet(size_t max_size); + ~RankedSet(); // For move semantics. - RankedList(RankedList&& other); - RankedList& operator=(RankedList&& other); + RankedSet(RankedSet&& other); + RankedSet& operator=(RankedSet&& other); // Accessors for begin() and end() const iterators. const_iterator begin() const { return entries_.begin(); } @@ -54,8 +58,12 @@ class RankedList { size_t size() const { return entries_.size(); } size_t max_size() const { return max_size_; } - // Add a new value-count pair to the list. Does not check for existing entries - // with the same value. Is an O(n) operation due to ordering. + // Add a new value-count pair to the container. Will overwrite any existing + // entry with the same value and count. Will not overwrite an existing entry + // with the same value but a different count, or different values with the + // same count. + // + // Time complexity is O(log n). void Add(const ValueType& value, int count); private: @@ -70,13 +78,13 @@ class RankedList { // Max number of items that can be stored in the list. size_t max_size_; - // Points to the array of entries. - std::list<Entry, STLAllocator<Entry, CustomAllocator>> entries_; + // Actual storage container for entries. + EntrySet entries_; - DISALLOW_COPY_AND_ASSIGN(RankedList); + DISALLOW_COPY_AND_ASSIGN(RankedSet); }; } // namespace leak_detector } // namespace metrics -#endif // COMPONENTS_METRICS_LEAK_DETECTOR_RANKED_LIST_H_ +#endif // COMPONENTS_METRICS_LEAK_DETECTOR_RANKED_SET_H_ diff --git a/components/metrics/leak_detector/ranked_set_unittest.cc b/components/metrics/leak_detector/ranked_set_unittest.cc new file mode 100644 index 0000000..2e2de01 --- /dev/null +++ b/components/metrics/leak_detector/ranked_set_unittest.cc @@ -0,0 +1,324 @@ +// 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/ranked_set.h" + +#include <stddef.h> +#include <stdint.h> + +#include <algorithm> + +#include "base/macros.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 leak_detector { + +namespace { + +// Makes it easier to instantiate LeakDetectorValueTypes. +LeakDetectorValueType Value(uint32_t value) { + return LeakDetectorValueType(value); +} + +} // namespace + +class RankedSetTest : public ::testing::Test { + public: + RankedSetTest() {} + + void SetUp() override { CustomAllocator::Initialize(); } + void TearDown() override { EXPECT_TRUE(CustomAllocator::Shutdown()); } + + private: + DISALLOW_COPY_AND_ASSIGN(RankedSetTest); +}; + +TEST_F(RankedSetTest, Iterators) { + RankedSet set(10); + EXPECT_TRUE(set.begin() == set.end()); + + set.Add(Value(0x1234), 100); + EXPECT_FALSE(set.begin() == set.end()); +} + +TEST_F(RankedSetTest, SingleInsertion) { + RankedSet set(10); + EXPECT_EQ(0U, set.size()); + + set.Add(Value(0x1234), 100); + EXPECT_EQ(1U, set.size()); + + auto iter = set.begin(); + EXPECT_EQ(0x1234U, iter->value.size()); + EXPECT_EQ(100, iter->count); +} + +TEST_F(RankedSetTest, InOrderInsertion) { + RankedSet set(10); + EXPECT_EQ(0U, set.size()); + + set.Add(Value(0x1234), 100); + EXPECT_EQ(1U, set.size()); + set.Add(Value(0x2345), 95); + EXPECT_EQ(2U, set.size()); + set.Add(Value(0x3456), 90); + EXPECT_EQ(3U, set.size()); + set.Add(Value(0x4567), 85); + EXPECT_EQ(4U, set.size()); + set.Add(Value(0x5678), 80); + EXPECT_EQ(5U, set.size()); + + // Iterate through the contents to make sure they match what went in. + const RankedSet::Entry kExpectedValues[] = { + {Value(0x1234), 100}, {Value(0x2345), 95}, {Value(0x3456), 90}, + {Value(0x4567), 85}, {Value(0x5678), 80}, + }; + + size_t index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues)); + EXPECT_EQ(kExpectedValues[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues[index].count, entry.count); + ++index; + } +} + +TEST_F(RankedSetTest, ReverseOrderInsertion) { + RankedSet set(10); + EXPECT_EQ(0U, set.size()); + + set.Add(Value(0x1234), 0); + EXPECT_EQ(1U, set.size()); + set.Add(Value(0x2345), 5); + EXPECT_EQ(2U, set.size()); + set.Add(Value(0x3456), 10); + EXPECT_EQ(3U, set.size()); + set.Add(Value(0x4567), 15); + EXPECT_EQ(4U, set.size()); + set.Add(Value(0x5678), 20); + EXPECT_EQ(5U, set.size()); + + // Iterate through the contents to make sure they match what went in. + const RankedSet::Entry kExpectedValues[] = { + {Value(0x5678), 20}, {Value(0x4567), 15}, {Value(0x3456), 10}, + {Value(0x2345), 5}, {Value(0x1234), 0}, + }; + + size_t index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues)); + EXPECT_EQ(kExpectedValues[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues[index].count, entry.count); + ++index; + } +} + +TEST_F(RankedSetTest, UnorderedInsertion) { + RankedSet set(10); + EXPECT_EQ(0U, set.size()); + + set.Add(Value(0x1234), 15); + set.Add(Value(0x2345), 20); + set.Add(Value(0x3456), 10); + set.Add(Value(0x4567), 30); + set.Add(Value(0x5678), 25); + EXPECT_EQ(5U, set.size()); + + // Iterate through the contents to make sure they match what went in. + const RankedSet::Entry kExpectedValues1[] = { + {Value(0x4567), 30}, {Value(0x5678), 25}, {Value(0x2345), 20}, + {Value(0x1234), 15}, {Value(0x3456), 10}, + }; + + size_t index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues1)); + EXPECT_EQ(kExpectedValues1[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues1[index].count, entry.count); + ++index; + } + + // Add more items. + set.Add(Value(0x6789), 35); + set.Add(Value(0x789a), 40); + set.Add(Value(0x89ab), 50); + set.Add(Value(0x9abc), 5); + set.Add(Value(0xabcd), 0); + EXPECT_EQ(10U, set.size()); + + // Iterate through the contents to make sure they match what went in. + const RankedSet::Entry kExpectedValues2[] = { + {Value(0x89ab), 50}, {Value(0x789a), 40}, {Value(0x6789), 35}, + {Value(0x4567), 30}, {Value(0x5678), 25}, {Value(0x2345), 20}, + {Value(0x1234), 15}, {Value(0x3456), 10}, {Value(0x9abc), 5}, + {Value(0xabcd), 0}, + }; + + index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues2)); + EXPECT_EQ(kExpectedValues2[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues2[index].count, entry.count); + ++index; + } +} + +TEST_F(RankedSetTest, UnorderedInsertionWithSameCounts) { + RankedSet set(10); + EXPECT_EQ(0U, set.size()); + + set.Add(Value(0x1234), 20); + set.Add(Value(0x2345), 20); + set.Add(Value(0x3456), 30); + set.Add(Value(0x4567), 30); + set.Add(Value(0x5678), 20); + EXPECT_EQ(5U, set.size()); + + // Iterate through the contents to make sure they match what went in. Entries + // with the same count should be ordered from lowest value to highest value. + const RankedSet::Entry kExpectedValues1[] = { + {Value(0x3456), 30}, {Value(0x4567), 30}, {Value(0x1234), 20}, + {Value(0x2345), 20}, {Value(0x5678), 20}, + }; + + size_t index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues1)); + EXPECT_EQ(kExpectedValues1[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues1[index].count, entry.count); + ++index; + } +} + +TEST_F(RankedSetTest, RepeatedEntries) { + RankedSet set(10); + EXPECT_EQ(0U, set.size()); + + set.Add(Value(0x1234), 20); + set.Add(Value(0x3456), 30); + set.Add(Value(0x1234), 20); + set.Add(Value(0x3456), 30); + set.Add(Value(0x4567), 30); + set.Add(Value(0x4567), 30); + EXPECT_EQ(3U, set.size()); + + // Iterate through the contents to make sure they match what went in. + const RankedSet::Entry kExpectedValues1[] = { + {Value(0x3456), 30}, {Value(0x4567), 30}, {Value(0x1234), 20}, + }; + + size_t index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues1)); + EXPECT_EQ(kExpectedValues1[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues1[index].count, entry.count); + ++index; + } +} + +TEST_F(RankedSetTest, InsertionWithOverflow) { + RankedSet set(5); + EXPECT_EQ(0U, set.size()); + + set.Add(Value(0x1234), 15); + set.Add(Value(0x2345), 20); + set.Add(Value(0x3456), 10); + set.Add(Value(0x4567), 30); + set.Add(Value(0x5678), 25); + EXPECT_EQ(5U, set.size()); + + // These values will not make it into the set, which is now full. + set.Add(Value(0x6789), 0); + EXPECT_EQ(5U, set.size()); + set.Add(Value(0x789a), 5); + EXPECT_EQ(5U, set.size()); + + // Iterate through the contents to make sure they match what went in. + const RankedSet::Entry kExpectedValues1[] = { + {Value(0x4567), 30}, {Value(0x5678), 25}, {Value(0x2345), 20}, + {Value(0x1234), 15}, {Value(0x3456), 10}, + }; + + size_t index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues1)); + EXPECT_EQ(kExpectedValues1[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues1[index].count, entry.count); + ++index; + } + + // Insert some more values that go in the middle of the set. + set.Add(Value(0x89ab), 27); + EXPECT_EQ(5U, set.size()); + set.Add(Value(0x9abc), 22); + EXPECT_EQ(5U, set.size()); + + // Iterate through the contents to make sure they match what went in. + const RankedSet::Entry kExpectedValues2[] = { + {Value(0x4567), 30}, {Value(0x89ab), 27}, {Value(0x5678), 25}, + {Value(0x9abc), 22}, {Value(0x2345), 20}, + }; + + index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues2)); + EXPECT_EQ(kExpectedValues2[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues2[index].count, entry.count); + ++index; + } + + // Insert some more values at the front of the set. + set.Add(Value(0xabcd), 40); + EXPECT_EQ(5U, set.size()); + set.Add(Value(0xbcde), 35); + EXPECT_EQ(5U, set.size()); + + // Iterate through the contents to make sure they match what went in. + const RankedSet::Entry kExpectedValues3[] = { + {Value(0xabcd), 40}, {Value(0xbcde), 35}, {Value(0x4567), 30}, + {Value(0x89ab), 27}, {Value(0x5678), 25}, + }; + + index = 0; + for (const auto& entry : set) { + EXPECT_LT(index, arraysize(kExpectedValues3)); + EXPECT_EQ(kExpectedValues3[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues3[index].count, entry.count); + ++index; + } +} + +TEST_F(RankedSetTest, MoveOperation) { + const RankedSet::Entry kExpectedValues[] = { + {Value(0x89ab), 50}, {Value(0x789a), 40}, {Value(0x6789), 35}, + {Value(0x4567), 30}, {Value(0x5678), 25}, {Value(0x2345), 20}, + {Value(0x1234), 15}, {Value(0x3456), 10}, {Value(0x9abc), 5}, + {Value(0xabcd), 0}, + }; + + RankedSet source(10); + for (const RankedSet::Entry& entry : kExpectedValues) { + source.Add(entry.value, entry.count); + } + EXPECT_EQ(10U, source.size()); + + RankedSet dest(25); // This should be changed by the move. + dest = std::move(source); + EXPECT_EQ(10U, dest.size()); + EXPECT_EQ(10U, dest.max_size()); + + size_t index = 0; + for (const auto& entry : dest) { + EXPECT_LT(index, arraysize(kExpectedValues)); + EXPECT_EQ(kExpectedValues[index].value.size(), entry.value.size()); + EXPECT_EQ(kExpectedValues[index].count, entry.count); + ++index; + } +} + +} // namespace leak_detector +} // namespace metrics |