summaryrefslogtreecommitdiffstats
path: root/components
diff options
context:
space:
mode:
authorsque <sque@chromium.org>2016-03-24 08:54:10 -0700
committerCommit bot <commit-bot@chromium.org>2016-03-24 15:55:05 +0000
commitadc00de0d546fe808b01f2d7e1984e6a52c9e3a4 (patch)
tree84658b79883923d072ede282904400f5ff34b041 /components
parent0f90ad8a2751b55f1193112652705d21bbb2a8d0 (diff)
downloadchromium_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.gyp2
-rw-r--r--components/metrics.gypi4
-rw-r--r--components/metrics/BUILD.gn6
-rw-r--r--components/metrics/leak_detector/call_stack_table.cc9
-rw-r--r--components/metrics/leak_detector/leak_analyzer.cc22
-rw-r--r--components/metrics/leak_detector/leak_analyzer.h16
-rw-r--r--components/metrics/leak_detector/leak_analyzer_unittest.cc256
-rw-r--r--components/metrics/leak_detector/leak_detector_impl.cc12
-rw-r--r--components/metrics/leak_detector/ranked_list.cc49
-rw-r--r--components/metrics/leak_detector/ranked_list_unittest.cc271
-rw-r--r--components/metrics/leak_detector/ranked_set.cc52
-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.cc324
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