diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-01-31 16:33:43 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2018-02-06 16:33:22 +0000 |
commit | da51f56cc21233c2d30f0fe0d171727c3102b2e0 (patch) | |
tree | 4e579ab70ce4b19bee7984237f3ce05a96d59d83 /chromium/base/metrics | |
parent | c8c2d1901aec01e934adf561a9fdf0cc776cdef8 (diff) | |
download | qtwebengine-chromium-da51f56cc21233c2d30f0fe0d171727c3102b2e0.tar.gz |
BASELINE: Update Chromium to 65.0.3525.40
Also imports missing submodules
Change-Id: I36901b7c6a325cda3d2c10cedb2186c25af3b79b
Reviewed-by: Alexandru Croitor <alexandru.croitor@qt.io>
Diffstat (limited to 'chromium/base/metrics')
20 files changed, 643 insertions, 755 deletions
diff --git a/chromium/base/metrics/field_trial.cc b/chromium/base/metrics/field_trial.cc index 72f25a9013e..7f7b372da00 100644 --- a/chromium/base/metrics/field_trial.cc +++ b/chromium/base/metrics/field_trial.cc @@ -199,7 +199,7 @@ void AddFeatureAndFieldTrialFlags(const char* enable_features_switch, cmd_line->AppendSwitchASCII(disable_features_switch, disabled_features); std::string field_trial_states; - FieldTrialList::AllStatesToString(&field_trial_states); + FieldTrialList::AllStatesToString(&field_trial_states, false); if (!field_trial_states.empty()) { cmd_line->AppendSwitchASCII(switches::kForceFieldTrials, field_trial_states); @@ -456,18 +456,9 @@ bool FieldTrial::GetActiveGroup(ActiveGroup* active_group) const { return true; } -bool FieldTrial::GetState(State* field_trial_state) { - if (!enable_field_trial_) - return false; - FinalizeGroupChoice(); - field_trial_state->trial_name = &trial_name_; - field_trial_state->group_name = &group_name_; - field_trial_state->activated = group_reported_; - return true; -} - -bool FieldTrial::GetStateWhileLocked(State* field_trial_state) { - if (!enable_field_trial_) +bool FieldTrial::GetStateWhileLocked(State* field_trial_state, + bool include_expired) { + if (!include_expired && !enable_field_trial_) return false; FinalizeGroupChoiceImpl(true); field_trial_state->trial_name = &trial_name_; @@ -648,14 +639,15 @@ void FieldTrialList::StatesToString(std::string* output) { } // static -void FieldTrialList::AllStatesToString(std::string* output) { +void FieldTrialList::AllStatesToString(std::string* output, + bool include_expired) { if (!global_) return; AutoLock auto_lock(global_->lock_); for (const auto& registered : global_->registered_) { FieldTrial::State trial; - if (!registered.second->GetStateWhileLocked(&trial)) + if (!registered.second->GetStateWhileLocked(&trial, include_expired)) continue; DCHECK_EQ(std::string::npos, trial.trial_name->find(kPersistentStringSeparator)); @@ -1335,7 +1327,7 @@ void FieldTrialList::AddToAllocatorWhileLocked( return; FieldTrial::State trial_state; - if (!field_trial->GetStateWhileLocked(&trial_state)) + if (!field_trial->GetStateWhileLocked(&trial_state, false)) return; // Or if we've already added it. We must check after GetState since it can diff --git a/chromium/base/metrics/field_trial.h b/chromium/base/metrics/field_trial.h index 19387a5f4c7..f794803f315 100644 --- a/chromium/base/metrics/field_trial.h +++ b/chromium/base/metrics/field_trial.h @@ -320,15 +320,13 @@ class BASE_EXPORT FieldTrial : public RefCounted<FieldTrial> { bool GetActiveGroup(ActiveGroup* active_group) const; // Returns the trial name and selected group name for this field trial via - // the output parameter |field_trial_state|, but only if the trial has not - // been disabled. In that case, true is returned and |field_trial_state| is - // filled in; otherwise, the result is false and |field_trial_state| is left - // untouched. - bool GetState(State* field_trial_state); - - // Does the same thing as above, but is deadlock-free if the caller is holding - // a lock. - bool GetStateWhileLocked(State* field_trial_state); + // the output parameter |field_trial_state| for all the studies when + // |bool include_expired| is true. In case when |bool include_expired| is + // false, if the trial has not been disabled true is returned and + // |field_trial_state| is filled in; otherwise, the result is false and + // |field_trial_state| is left untouched. + // This function is deadlock-free if the caller is holding a lock. + bool GetStateWhileLocked(State* field_trial_state, bool include_expired); // Returns the group_name. A winner need not have been chosen. std::string group_name_internal() const { return group_name_; } @@ -506,11 +504,11 @@ class BASE_EXPORT FieldTrialList { // resurrection in another process. This allows randomization to be done in // one process, and secondary processes can be synchronized on the result. // The resulting string contains the name and group name pairs of all - // registered FieldTrials which have not been disabled, with "/" used - // to separate all names and to terminate the string. All activated trials - // have their name prefixed with "*". This string is parsed by - // |CreateTrialsFromString()|. - static void AllStatesToString(std::string* output); + // registered FieldTrials including disabled based on |include_expired|, + // with "/" used to separate all names and to terminate the string. All + // activated trials have their name prefixed with "*". This string is parsed + // by |CreateTrialsFromString()|. + static void AllStatesToString(std::string* output, bool include_expired); // Fills in the supplied vector |active_groups| (which must be empty when // called) with a snapshot of all registered FieldTrials for which the group diff --git a/chromium/base/metrics/field_trial_unittest.cc b/chromium/base/metrics/field_trial_unittest.cc index 42c3ba1e436..56f727840d2 100644 --- a/chromium/base/metrics/field_trial_unittest.cc +++ b/chromium/base/metrics/field_trial_unittest.cc @@ -86,8 +86,7 @@ class FieldTrialTest : public testing::Test { DISALLOW_COPY_AND_ASSIGN(FieldTrialTest); }; -// Test registration, and also check that destructors are called for trials -// (and that Valgrind doesn't catch us leaking). +// Test registration, and also check that destructors are called for trials. TEST_F(FieldTrialTest, Registration) { const char name1[] = "name 1 test"; const char name2[] = "name 2 test"; @@ -334,36 +333,6 @@ TEST_F(FieldTrialTest, GetActiveFieldTrialGroupsFromString) { EXPECT_EQ("Z", active_groups[1].group_name); } -TEST_F(FieldTrialTest, AllGroups) { - FieldTrial::State field_trial_state; - std::string one_winner("One Winner"); - scoped_refptr<FieldTrial> trial = - CreateFieldTrial(one_winner, 10, "Default", nullptr); - std::string winner("Winner"); - trial->AppendGroup(winner, 10); - EXPECT_TRUE(trial->GetState(&field_trial_state)); - EXPECT_EQ(one_winner, *field_trial_state.trial_name); - EXPECT_EQ(winner, *field_trial_state.group_name); - trial->group(); - EXPECT_TRUE(trial->GetState(&field_trial_state)); - EXPECT_EQ(one_winner, *field_trial_state.trial_name); - EXPECT_EQ(winner, *field_trial_state.group_name); - - std::string multi_group("MultiGroup"); - scoped_refptr<FieldTrial> multi_group_trial = - CreateFieldTrial(multi_group, 9, "Default", nullptr); - - multi_group_trial->AppendGroup("Me", 3); - multi_group_trial->AppendGroup("You", 3); - multi_group_trial->AppendGroup("Them", 3); - EXPECT_TRUE(multi_group_trial->GetState(&field_trial_state)); - // Finalize the group selection by accessing the selected group. - multi_group_trial->group(); - EXPECT_TRUE(multi_group_trial->GetState(&field_trial_state)); - EXPECT_EQ(multi_group, *field_trial_state.trial_name); - EXPECT_EQ(multi_group_trial->group_name(), *field_trial_state.group_name); -} - TEST_F(FieldTrialTest, ActiveGroupsNotFinalized) { const char kTrialName[] = "TestTrial"; const char kSecondaryGroupName[] = "SecondaryGroup"; @@ -469,7 +438,7 @@ TEST_F(FieldTrialTest, SaveAll) { scoped_refptr<FieldTrial> trial = CreateFieldTrial("Some name", 10, "Default some name", nullptr); EXPECT_EQ("", trial->group_name_internal()); - FieldTrialList::AllStatesToString(&save_string); + FieldTrialList::AllStatesToString(&save_string, false); EXPECT_EQ("Some name/Default some name/", save_string); // Getting all states should have finalized the trial. EXPECT_EQ("Default some name", trial->group_name_internal()); @@ -480,7 +449,7 @@ TEST_F(FieldTrialTest, SaveAll) { trial->AppendGroup("Winner", 10); // Finalize the group selection by accessing the selected group. trial->group(); - FieldTrialList::AllStatesToString(&save_string); + FieldTrialList::AllStatesToString(&save_string, false); EXPECT_EQ("Some name/Default some name/*trial2/Winner/", save_string); save_string.clear(); @@ -491,7 +460,7 @@ TEST_F(FieldTrialTest, SaveAll) { // Finalize the group selection by accessing the selected group. trial2->group(); - FieldTrialList::AllStatesToString(&save_string); + FieldTrialList::AllStatesToString(&save_string, false); // We assume names are alphabetized... though this is not critical. EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/", save_string); @@ -501,9 +470,29 @@ TEST_F(FieldTrialTest, SaveAll) { scoped_refptr<FieldTrial> trial3 = CreateFieldTrial("zzz", 10, "default", nullptr); - FieldTrialList::AllStatesToString(&save_string); + FieldTrialList::AllStatesToString(&save_string, false); + EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", + save_string); + + // Create expired study. + int default_group_number = -1; + scoped_refptr<FieldTrial> expired_trial = + FieldTrialList::FactoryGetFieldTrial( + "Expired trial name", 1000000000, "Default group", + OneYearBeforeBuildTime(), 1, 1, base::FieldTrial::SESSION_RANDOMIZED, + &default_group_number); + expired_trial->AppendGroup("Expired trial group name", 999999999); + + save_string.clear(); + FieldTrialList::AllStatesToString(&save_string, false); EXPECT_EQ("Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", save_string); + save_string.clear(); + FieldTrialList::AllStatesToString(&save_string, true); + EXPECT_EQ( + "Expired trial name/Default group/" + "Some name/Default some name/*trial2/Winner/*xxx/yyyy/zzz/default/", + save_string); } TEST_F(FieldTrialTest, Restore) { @@ -1109,7 +1098,7 @@ TEST(FieldTrialTestWithoutList, StatesStringFormat) { scoped_refptr<FieldTrial> trial3 = CreateFieldTrial("zzz", 10, "default", nullptr); - FieldTrialList::AllStatesToString(&save_string); + FieldTrialList::AllStatesToString(&save_string, false); } // Starting with a new blank FieldTrialList. @@ -1192,7 +1181,7 @@ TEST(FieldTrialListTest, AddTrialsToAllocator) { FieldTrialList field_trial_list(nullptr); FieldTrialList::CreateFieldTrial("Trial1", "Group1"); FieldTrialList::InstantiateFieldTrialAllocatorIfNeeded(); - FieldTrialList::AllStatesToString(&save_string); + FieldTrialList::AllStatesToString(&save_string, false); handle = base::SharedMemory::DuplicateHandle( field_trial_list.field_trial_allocator_->shared_memory()->handle()); } @@ -1203,7 +1192,7 @@ TEST(FieldTrialListTest, AddTrialsToAllocator) { shm.get()->Map(4 << 10); FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); std::string check_string; - FieldTrialList::AllStatesToString(&check_string); + FieldTrialList::AllStatesToString(&check_string, false); EXPECT_EQ(save_string, check_string); } @@ -1241,7 +1230,7 @@ TEST(FieldTrialListTest, DoNotAddSimulatedFieldTrialsToAllocator) { shm.get()->Map(4 << 10); FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); std::string check_string; - FieldTrialList::AllStatesToString(&check_string); + FieldTrialList::AllStatesToString(&check_string, false); ASSERT_EQ(check_string.find("Simulated"), std::string::npos); } @@ -1325,7 +1314,7 @@ TEST(FieldTrialListTest, ClearParamsFromSharedMemory) { shm.get()->Map(4 << 10); FieldTrialList::CreateTrialsFromSharedMemory(std::move(shm)); std::string check_string; - FieldTrialList::AllStatesToString(&check_string); + FieldTrialList::AllStatesToString(&check_string, false); EXPECT_EQ("*Trial1/Group1/", check_string); } diff --git a/chromium/base/metrics/histogram.cc b/chromium/base/metrics/histogram.cc index 40e7bcc860a..488facd066d 100644 --- a/chromium/base/metrics/histogram.cc +++ b/chromium/base/metrics/histogram.cc @@ -19,10 +19,9 @@ #include "base/compiler_specific.h" #include "base/debug/alias.h" -#include "base/debug/crash_logging.h" #include "base/logging.h" #include "base/memory/ptr_util.h" -#include "base/metrics/histogram_macros.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/metrics_hashes.h" #include "base/metrics/persistent_histogram_allocator.h" #include "base/metrics/persistent_memory_allocator.h" @@ -438,8 +437,8 @@ bool Histogram::InspectConstructionArguments(StringPiece name, } if (!check_okay) { - UMA_HISTOGRAM_SPARSE_SLOWLY("Histogram.BadConstructionArguments", - static_cast<Sample>(HashMetricName(name))); + UmaHistogramSparse("Histogram.BadConstructionArguments", + static_cast<Sample>(HashMetricName(name))); } return check_okay; @@ -575,9 +574,6 @@ bool Histogram::ValidateHistogramContents(bool crash_if_invalid, // Abort if a problem is found (except "flags", which could legally be zero). std::string debug_string = base::StringPrintf( "%s/%" PRIu32 "#%d", histogram_name(), bad_fields, identifier); -#if !defined(OS_NACL) - base::debug::ScopedCrashKey crash_key("bad_histogram", debug_string); -#endif CHECK(false) << debug_string; debug::Alias(&bad_fields); return false; diff --git a/chromium/base/metrics/histogram_flattener.h b/chromium/base/metrics/histogram_flattener.h index 22d9a92d32b..6a5e3f42988 100644 --- a/chromium/base/metrics/histogram_flattener.h +++ b/chromium/base/metrics/histogram_flattener.h @@ -23,8 +23,8 @@ class BASE_EXPORT HistogramFlattener { const HistogramSamples& snapshot) = 0; protected: - HistogramFlattener() {} - virtual ~HistogramFlattener() {} + HistogramFlattener() = default; + virtual ~HistogramFlattener() = default; private: DISALLOW_COPY_AND_ASSIGN(HistogramFlattener); diff --git a/chromium/base/metrics/histogram_functions.cc b/chromium/base/metrics/histogram_functions.cc index 4c1a4b57ed2..47eec7dbbfd 100644 --- a/chromium/base/metrics/histogram_functions.cc +++ b/chromium/base/metrics/histogram_functions.cc @@ -6,13 +6,14 @@ #include "base/metrics/histogram.h" #include "base/metrics/histogram_base.h" +#include "base/metrics/sparse_histogram.h" #include "base/time/time.h" namespace base { void UmaHistogramBoolean(const std::string& name, bool sample) { HistogramBase* histogram = BooleanHistogram::FactoryGet( - name, base::HistogramBase::kUmaTargetedHistogramFlag); + name, HistogramBase::kUmaTargetedHistogramFlag); histogram->Add(sample); } @@ -100,4 +101,10 @@ void UmaHistogramMemoryLargeMB(const std::string& name, int sample) { UmaHistogramCustomCounts(name, sample, 1, 64000, 100); } +void UmaHistogramSparse(const std::string& name, int sample) { + HistogramBase* histogram = SparseHistogram::FactoryGet( + name, HistogramBase::kUmaTargetedHistogramFlag); + histogram->Add(sample); +} + } // namespace base diff --git a/chromium/base/metrics/histogram_functions.h b/chromium/base/metrics/histogram_functions.h index 46986283880..c9632fa26f0 100644 --- a/chromium/base/metrics/histogram_functions.h +++ b/chromium/base/metrics/histogram_functions.h @@ -110,6 +110,35 @@ BASE_EXPORT void UmaHistogramMemoryMB(const std::string& name, int sample); // Used to measure common MB-granularity memory stats. Range is up to ~64G. BASE_EXPORT void UmaHistogramMemoryLargeMB(const std::string& name, int sample); +// For recording sparse histograms. +// The |sample| can be a negative or non-negative number. +// +// Sparse histograms are well suited for recording counts of exact sample values +// that are sparsely distributed over a relatively large range, in cases where +// ultra-fast performance is not critical. For instance, Sqlite.Version.* are +// sparse because for any given database, there's going to be exactly one +// version logged. +// +// Performance: +// ------------ +// Sparse histograms are typically more memory-efficient but less time-efficient +// than other histograms. Essentially, they sparse histograms use a map rather +// than a vector for their backing storage; they also require lock acquisition +// to increment a sample, whereas other histogram do not. Hence, each increment +// operation is a bit slower than for other histograms. But, if the data is +// sparse, then they use less memory client-side, because they allocate buckets +// on demand rather than preallocating. +// +// Data size: +// ---------- +// Note that server-side, we still need to load all buckets, across all users, +// at once. Thus, please avoid exploding such histograms, i.e. uploading many +// many distinct values to the server (across all users). Concretely, keep the +// number of distinct values <= 100 ideally, definitely <= 1000. If you have no +// guarantees on the range of your data, use clamping, e.g.: +// UmaHistogramSparse("MyHistogram", ClampToRange(value, 0, 200)); +BASE_EXPORT void UmaHistogramSparse(const std::string& name, int sample); + } // namespace base #endif // BASE_METRICS_HISTOGRAM_FUNCTIONS_H_ diff --git a/chromium/base/metrics/histogram_functions_unittest.cc b/chromium/base/metrics/histogram_functions_unittest.cc index 7bfd202e2dc..37206747572 100644 --- a/chromium/base/metrics/histogram_functions_unittest.cc +++ b/chromium/base/metrics/histogram_functions_unittest.cc @@ -7,6 +7,7 @@ #include "base/metrics/histogram_macros.h" #include "base/test/histogram_tester.h" #include "base/time/time.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -17,7 +18,7 @@ enum UmaHistogramTestingEnum { UMA_HISTOGRAM_TESTING_ENUM_THIRD }; -TEST(HistogramFunctionsTest, HistogramExactLinear) { +TEST(HistogramFunctionsTest, ExactLinear) { std::string histogram("Testing.UMA.HistogramExactLinear"); HistogramTester tester; UmaHistogramExactLinear(histogram, 10, 100); @@ -37,7 +38,7 @@ TEST(HistogramFunctionsTest, HistogramExactLinear) { tester.ExpectTotalCount(histogram, 5); } -TEST(HistogramFunctionsTest, HistogramEnumeration) { +TEST(HistogramFunctionsTest, Enumeration) { std::string histogram("Testing.UMA.HistogramEnumeration"); HistogramTester tester; UmaHistogramEnumeration(histogram, UMA_HISTOGRAM_TESTING_ENUM_FIRST, @@ -53,7 +54,7 @@ TEST(HistogramFunctionsTest, HistogramEnumeration) { tester.ExpectTotalCount(histogram, 2); } -TEST(HistogramFunctionsTest, HistogramBoolean) { +TEST(HistogramFunctionsTest, Boolean) { std::string histogram("Testing.UMA.HistogramBoolean"); HistogramTester tester; UmaHistogramBoolean(histogram, true); @@ -63,7 +64,7 @@ TEST(HistogramFunctionsTest, HistogramBoolean) { tester.ExpectTotalCount(histogram, 2); } -TEST(HistogramFunctionsTest, HistogramPercentage) { +TEST(HistogramFunctionsTest, Percentage) { std::string histogram("Testing.UMA.HistogramPercentage"); HistogramTester tester; UmaHistogramPercentage(histogram, 50); @@ -74,7 +75,7 @@ TEST(HistogramFunctionsTest, HistogramPercentage) { tester.ExpectTotalCount(histogram, 2); } -TEST(HistogramFunctionsTest, HistogramCounts) { +TEST(HistogramFunctionsTest, Counts) { std::string histogram("Testing.UMA.HistogramCount.Custom"); HistogramTester tester; UmaHistogramCustomCounts(histogram, 10, 1, 100, 10); @@ -89,7 +90,7 @@ TEST(HistogramFunctionsTest, HistogramCounts) { tester.ExpectTotalCount(histogram, 5); } -TEST(HistogramFunctionsTest, HistogramTimes) { +TEST(HistogramFunctionsTest, Times) { std::string histogram("Testing.UMA.HistogramTimes"); HistogramTester tester; UmaHistogramTimes(histogram, TimeDelta::FromSeconds(1)); @@ -106,4 +107,21 @@ TEST(HistogramFunctionsTest, HistogramTimes) { tester.ExpectTotalCount(histogram, 4); } +TEST(HistogramFunctionsTest, Sparse_SupportsLargeRange) { + std::string histogram("Testing.UMA.HistogramSparse"); + HistogramTester tester; + UmaHistogramSparse(histogram, 0); + UmaHistogramSparse(histogram, 123456789); + UmaHistogramSparse(histogram, 123456789); + EXPECT_THAT(tester.GetAllSamples(histogram), + testing::ElementsAre(Bucket(0, 1), Bucket(123456789, 2))); +} + +TEST(HistogramFunctionsTest, Sparse_SupportsNegativeValues) { + std::string histogram("Testing.UMA.HistogramSparse"); + HistogramTester tester; + UmaHistogramSparse(histogram, -1); + tester.ExpectUniqueSample(histogram, -1, 1); +} + } // namespace base. diff --git a/chromium/base/metrics/histogram_macros.h b/chromium/base/metrics/histogram_macros.h index 891d2a40bff..083bae753cb 100644 --- a/chromium/base/metrics/histogram_macros.h +++ b/chromium/base/metrics/histogram_macros.h @@ -249,32 +249,6 @@ base::HistogramBase::kUmaStabilityHistogramFlag) //------------------------------------------------------------------------------ -// Sparse histograms. - -// Sparse histograms are well suited for recording counts of exact sample values -// that are sparsely distributed over a large range. -// -// UMA_HISTOGRAM_SPARSE_SLOWLY is good for sparsely distributed and/or -// infrequently recorded values since the implementation is slower -// and takes more memory. For sparse data, sparse histograms have the advantage -// of using less memory client-side, because they allocate buckets on demand -// rather than preallocating. However, server-side, we still need to load all -// buckets, across all users, at once. - -// Thus, please avoid exploding such histograms, i.e. uploading many many -// distinct values to the server (across all users). Concretely, keep the number -// of distinct values <= 100 at best, definitely <= 1000. If you have no -// guarantees on the range of your data, use capping, e.g.: -// UMA_HISTOGRAM_SPARSE_SLOWLY("MyHistogram", -// std::max(0, std::min(200, value))); -// -// For instance, Sqlite.Version.* are sparse because for any given database, -// there's going to be exactly one version logged. -// The |sample| can be a negative or non-negative number. -#define UMA_HISTOGRAM_SPARSE_SLOWLY(name, sample) \ - INTERNAL_HISTOGRAM_SPARSE_SLOWLY(name, sample) - -//------------------------------------------------------------------------------ // Histogram instantiation helpers. // Support a collection of histograms, perhaps one for each entry in an diff --git a/chromium/base/metrics/histogram_macros_internal.h b/chromium/base/metrics/histogram_macros_internal.h index 15e191712ac..84defae32f6 100644 --- a/chromium/base/metrics/histogram_macros_internal.h +++ b/chromium/base/metrics/histogram_macros_internal.h @@ -180,16 +180,4 @@ base::TimeTicks constructed_; \ } scoped_histogram_timer_##key -// Macro for sparse histogram. -// The implementation is more costly to add values to, and each value -// stored has more overhead, compared to the other histogram types. However it -// may be more efficient in memory if the total number of sample values is small -// compared to the range of their values. -#define INTERNAL_HISTOGRAM_SPARSE_SLOWLY(name, sample) \ - do { \ - base::HistogramBase* histogram = base::SparseHistogram::FactoryGet( \ - name, base::HistogramBase::kUmaTargetedHistogramFlag); \ - histogram->Add(sample); \ - } while (0) - #endif // BASE_METRICS_HISTOGRAM_MACROS_INTERNAL_H_ diff --git a/chromium/base/metrics/histogram_samples.cc b/chromium/base/metrics/histogram_samples.cc index 7703580538f..6830637c06d 100644 --- a/chromium/base/metrics/histogram_samples.cc +++ b/chromium/base/metrics/histogram_samples.cc @@ -7,6 +7,7 @@ #include <limits> #include "base/compiler_specific.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_macros.h" #include "base/numerics/safe_conversions.h" #include "base/numerics/safe_math.h" @@ -258,8 +259,8 @@ void HistogramSamples::RecordNegativeSample(NegativeSampleReason reason, MAX_NEGATIVE_SAMPLE_REASONS); UMA_HISTOGRAM_CUSTOM_COUNTS("UMA.NegativeSamples.Increment", increment, 1, 1 << 30, 100); - UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.NegativeSamples.Histogram", - static_cast<int32_t>(id())); + UmaHistogramSparse("UMA.NegativeSamples.Histogram", + static_cast<int32_t>(id())); } SampleCountIterator::~SampleCountIterator() = default; diff --git a/chromium/base/metrics/histogram_snapshot_manager.h b/chromium/base/metrics/histogram_snapshot_manager.h index 51bf92c7b55..e2a404fa541 100644 --- a/chromium/base/metrics/histogram_snapshot_manager.h +++ b/chromium/base/metrics/histogram_snapshot_manager.h @@ -28,10 +28,10 @@ class HistogramFlattener; // corruption, this class also validates as much redundancy as it can before // calling for the marginal change (a.k.a., delta) in a histogram to be // recorded. -class BASE_EXPORT HistogramSnapshotManager { +class BASE_EXPORT HistogramSnapshotManager final { public: explicit HistogramSnapshotManager(HistogramFlattener* histogram_flattener); - virtual ~HistogramSnapshotManager(); + ~HistogramSnapshotManager(); // Snapshot all histograms, and ask |histogram_flattener_| to record the // delta. |flags_to_set| is used to set flags for each histogram. @@ -39,15 +39,13 @@ class BASE_EXPORT HistogramSnapshotManager { // Only histograms that have all the flags specified by the argument will be // chosen. If all histograms should be recorded, set it to // |Histogram::kNoFlags|. - template <class ForwardHistogramIterator> - void PrepareDeltas(ForwardHistogramIterator begin, - ForwardHistogramIterator end, + void PrepareDeltas(const std::vector<HistogramBase*>& histograms, HistogramBase::Flags flags_to_set, HistogramBase::Flags required_flags) { - for (ForwardHistogramIterator it = begin; it != end; ++it) { - (*it)->SetFlags(flags_to_set); - if (((*it)->flags() & required_flags) == required_flags) - PrepareDelta(*it); + for (HistogramBase* const histogram : histograms) { + histogram->SetFlags(flags_to_set); + if ((histogram->flags() & required_flags) == required_flags) + PrepareDelta(histogram); } } diff --git a/chromium/base/metrics/persistent_histogram_allocator.cc b/chromium/base/metrics/persistent_histogram_allocator.cc index 79a903eb183..6178b21a48d 100644 --- a/chromium/base/metrics/persistent_histogram_allocator.cc +++ b/chromium/base/metrics/persistent_histogram_allocator.cc @@ -1090,9 +1090,6 @@ GlobalHistogramAllocator::GlobalHistogramAllocator( std::unique_ptr<PersistentMemoryAllocator> memory) : PersistentHistogramAllocator(std::move(memory)), import_iterator_(this) { - // Make sure the StatisticsRecorder is initialized to prevent duplicate - // histograms from being created. It's safe to call this multiple times. - StatisticsRecorder::Initialize(); } void GlobalHistogramAllocator::ImportHistogramsToStatisticsRecorder() { diff --git a/chromium/base/metrics/persistent_memory_allocator.cc b/chromium/base/metrics/persistent_memory_allocator.cc index be107c39474..065c0282921 100644 --- a/chromium/base/metrics/persistent_memory_allocator.cc +++ b/chromium/base/metrics/persistent_memory_allocator.cc @@ -8,6 +8,7 @@ #include <algorithm> #if defined(OS_WIN) +#include <windows.h> #include "winbase.h" #elif defined(OS_POSIX) #include <sys/mman.h> @@ -16,7 +17,7 @@ #include "base/files/memory_mapped_file.h" #include "base/logging.h" #include "base/memory/shared_memory.h" -#include "base/metrics/histogram_macros.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/sparse_histogram.h" #include "base/numerics/safe_conversions.h" #include "base/sys_info.h" @@ -966,8 +967,8 @@ LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) { ::VirtualAlloc(nullptr, size, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE); if (address) return Memory(address, MEM_VIRTUAL); - UMA_HISTOGRAM_SPARSE_SLOWLY("UMA.LocalPersistentMemoryAllocator.Failures.Win", - ::GetLastError()); + UmaHistogramSparse("UMA.LocalPersistentMemoryAllocator.Failures.Win", + ::GetLastError()); #elif defined(OS_POSIX) // MAP_ANON is deprecated on Linux but MAP_ANONYMOUS is not universal on Mac. // MAP_SHARED is not available on Linux <2.4 but required on Mac. @@ -975,8 +976,8 @@ LocalPersistentMemoryAllocator::AllocateLocalMemory(size_t size) { MAP_ANON | MAP_SHARED, -1, 0); if (address != MAP_FAILED) return Memory(address, MEM_VIRTUAL); - UMA_HISTOGRAM_SPARSE_SLOWLY( - "UMA.LocalPersistentMemoryAllocator.Failures.Posix", errno); + UmaHistogramSparse("UMA.LocalPersistentMemoryAllocator.Failures.Posix", + errno); #else #error This architecture is not (yet) supported. #endif diff --git a/chromium/base/metrics/record_histogram_checker.h b/chromium/base/metrics/record_histogram_checker.h index 686bf676c63..75bc336d18e 100644 --- a/chromium/base/metrics/record_histogram_checker.h +++ b/chromium/base/metrics/record_histogram_checker.h @@ -15,7 +15,7 @@ namespace base { // the given histogram should be recorded. class BASE_EXPORT RecordHistogramChecker { public: - virtual ~RecordHistogramChecker() {} + virtual ~RecordHistogramChecker() = default; // Returns true iff the given histogram should be recorded. // This method may be called on any thread, so it should not mutate any state. diff --git a/chromium/base/metrics/single_sample_metrics.h b/chromium/base/metrics/single_sample_metrics.h index 6bfd7cb0db1..b966cb1ac5f 100644 --- a/chromium/base/metrics/single_sample_metrics.h +++ b/chromium/base/metrics/single_sample_metrics.h @@ -17,7 +17,7 @@ namespace base { // and destroyed from the same thread as construction. class BASE_EXPORT SingleSampleMetric { public: - virtual ~SingleSampleMetric() {} + virtual ~SingleSampleMetric() = default; virtual void SetSample(HistogramBase::Sample sample) = 0; }; @@ -34,7 +34,7 @@ class BASE_EXPORT SingleSampleMetric { // base/metrics/histogram.h for full parameter definitions. class BASE_EXPORT SingleSampleMetricsFactory { public: - virtual ~SingleSampleMetricsFactory() {} + virtual ~SingleSampleMetricsFactory() = default; // Returns the factory provided by SetFactory(), or if no factory has been set // a default factory will be provided (future calls to SetFactory() will fail @@ -63,8 +63,8 @@ class BASE_EXPORT SingleSampleMetricsFactory { class BASE_EXPORT DefaultSingleSampleMetricsFactory : public SingleSampleMetricsFactory { public: - DefaultSingleSampleMetricsFactory() {} - ~DefaultSingleSampleMetricsFactory() override {} + DefaultSingleSampleMetricsFactory() = default; + ~DefaultSingleSampleMetricsFactory() override = default; // SingleSampleMetricsFactory: std::unique_ptr<SingleSampleMetric> CreateCustomCountsMetric( diff --git a/chromium/base/metrics/sparse_histogram_unittest.cc b/chromium/base/metrics/sparse_histogram_unittest.cc index eeba150bf14..5f333e07eef 100644 --- a/chromium/base/metrics/sparse_histogram_unittest.cc +++ b/chromium/base/metrics/sparse_histogram_unittest.cc @@ -8,7 +8,7 @@ #include <string> #include "base/metrics/histogram_base.h" -#include "base/metrics/histogram_macros.h" +#include "base/metrics/histogram_functions.h" #include "base/metrics/histogram_samples.h" #include "base/metrics/metrics_hashes.h" #include "base/metrics/persistent_histogram_allocator.h" @@ -17,6 +17,7 @@ #include "base/metrics/statistics_recorder.h" #include "base/pickle.h" #include "base/strings/stringprintf.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace base { @@ -172,15 +173,15 @@ TEST_P(SparseHistogramTest, AddCount_LargeCountsDontOverflow) { } TEST_P(SparseHistogramTest, MacroBasicTest) { - UMA_HISTOGRAM_SPARSE_SLOWLY("Sparse", 100); - UMA_HISTOGRAM_SPARSE_SLOWLY("Sparse", 200); - UMA_HISTOGRAM_SPARSE_SLOWLY("Sparse", 100); + UmaHistogramSparse("Sparse", 100); + UmaHistogramSparse("Sparse", 200); + UmaHistogramSparse("Sparse", 100); - StatisticsRecorder::Histograms histograms; - StatisticsRecorder::GetHistograms(&histograms); + const StatisticsRecorder::Histograms histograms = + StatisticsRecorder::GetHistograms(); - ASSERT_EQ(1U, histograms.size()); - HistogramBase* sparse_histogram = histograms[0]; + ASSERT_THAT(histograms, testing::SizeIs(1)); + const HistogramBase* const sparse_histogram = histograms[0]; EXPECT_EQ(SPARSE_HISTOGRAM, sparse_histogram->GetHistogramType()); EXPECT_EQ("Sparse", StringPiece(sparse_histogram->histogram_name())); @@ -201,18 +202,14 @@ TEST_P(SparseHistogramTest, MacroInLoopTest) { // Unlike the macros in histogram.h, SparseHistogram macros can have a // variable as histogram name. for (int i = 0; i < 2; i++) { - std::string name = StringPrintf("Sparse%d", i + 1); - UMA_HISTOGRAM_SPARSE_SLOWLY(name, 100); + UmaHistogramSparse(StringPrintf("Sparse%d", i), 100); } - StatisticsRecorder::Histograms histograms; - StatisticsRecorder::GetHistograms(&histograms); - ASSERT_EQ(2U, histograms.size()); - - std::string name1 = histograms[0]->histogram_name(); - std::string name2 = histograms[1]->histogram_name(); - EXPECT_TRUE(("Sparse1" == name1 && "Sparse2" == name2) || - ("Sparse2" == name1 && "Sparse1" == name2)); + const StatisticsRecorder::Histograms histograms = + StatisticsRecorder::GetHistograms(); + ASSERT_THAT(histograms, testing::SizeIs(2)); + EXPECT_STREQ(histograms[0]->histogram_name(), "Sparse0"); + EXPECT_STREQ(histograms[1]->histogram_name(), "Sparse1"); } TEST_P(SparseHistogramTest, Serialize) { diff --git a/chromium/base/metrics/statistics_recorder.cc b/chromium/base/metrics/statistics_recorder.cc index a21adc09a0c..60e430eb72a 100644 --- a/chromium/base/metrics/statistics_recorder.cc +++ b/chromium/base/metrics/statistics_recorder.cc @@ -20,184 +20,138 @@ #include "base/strings/stringprintf.h" #include "base/values.h" +namespace base { namespace { -// Initialize histogram statistics gathering system. -base::LazyInstance<base::StatisticsRecorder>::Leaky g_statistics_recorder_ = - LAZY_INSTANCE_INITIALIZER; - bool HistogramNameLesser(const base::HistogramBase* a, const base::HistogramBase* b) { - return a->histogram_name() < b->histogram_name(); + return strcmp(a->histogram_name(), b->histogram_name()) < 0; } } // namespace -namespace base { +// static +LazyInstance<Lock>::Leaky StatisticsRecorder::lock_; -StatisticsRecorder::~StatisticsRecorder() { - DCHECK(histograms_); - DCHECK(ranges_); +// static +StatisticsRecorder* StatisticsRecorder::top_ = nullptr; - // Clean out what this object created and then restore what existed before. - Reset(); - base::AutoLock auto_lock(lock_.Get()); - histograms_ = existing_histograms_.release(); - callbacks_ = existing_callbacks_.release(); - ranges_ = existing_ranges_.release(); - providers_ = existing_providers_.release(); - record_checker_ = existing_record_checker_.release(); +// static +bool StatisticsRecorder::is_vlog_initialized_ = false; + +size_t StatisticsRecorder::BucketRangesHash::operator()( + const BucketRanges* const a) const { + return a->checksum(); } -// static -void StatisticsRecorder::Initialize() { - // Tests sometimes create local StatisticsRecorders in order to provide a - // contained environment of histograms that can be later discarded. If a - // true global instance gets created in this environment then it will - // eventually get disconnected when the local instance destructs and - // restores the previous state, resulting in no StatisticsRecorder at all. - // The global lazy instance, however, will remain valid thus ensuring that - // another never gets installed via this method. If a |histograms_| map - // exists then assume the StatisticsRecorder is already "initialized". - if (histograms_) - return; +bool StatisticsRecorder::BucketRangesEqual::operator()( + const BucketRanges* const a, + const BucketRanges* const b) const { + return a->Equals(b); +} - // Ensure that an instance of the StatisticsRecorder object is created. - g_statistics_recorder_.Get(); +StatisticsRecorder::~StatisticsRecorder() { + const AutoLock auto_lock(lock_.Get()); + DCHECK_EQ(this, top_); + top_ = previous_; } // static -bool StatisticsRecorder::IsActive() { - base::AutoLock auto_lock(lock_.Get()); - return histograms_ != nullptr; +void StatisticsRecorder::EnsureGlobalRecorderWhileLocked() { + lock_.Get().AssertAcquired(); + if (top_) + return; + + const StatisticsRecorder* const p = new StatisticsRecorder; + // The global recorder is never deleted. + ANNOTATE_LEAKING_OBJECT_PTR(p); + DCHECK_EQ(p, top_); } // static void StatisticsRecorder::RegisterHistogramProvider( const WeakPtr<HistogramProvider>& provider) { - providers_->push_back(provider); + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + top_->providers_.push_back(provider); } // static HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( HistogramBase* histogram) { - HistogramBase* histogram_to_delete = nullptr; - HistogramBase* histogram_to_return = nullptr; - { - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) { - histogram_to_return = histogram; - - // As per crbug.com/79322 the histograms are intentionally leaked, so we - // need to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used - // only once for an object, the duplicates should not be annotated. - // Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr) - // twice |if (!histograms_)|. - ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 - } else { - const char* name = histogram->histogram_name(); - StringPiece name_piece(name); - HistogramMap::iterator it = histograms_->find(name_piece); - if (histograms_->end() == it) { - // |name_piece| is guaranteed to never change or be deallocated so long - // as the histogram is alive (which is forever). - (*histograms_)[name_piece] = histogram; - ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 - // If there are callbacks for this histogram, we set the kCallbackExists - // flag. - auto callback_iterator = callbacks_->find(name); - if (callback_iterator != callbacks_->end()) { - if (!callback_iterator->second.is_null()) - histogram->SetFlags(HistogramBase::kCallbackExists); - else - histogram->ClearFlags(HistogramBase::kCallbackExists); - } - histogram_to_return = histogram; - } else if (histogram == it->second) { - // The histogram was registered before. - histogram_to_return = histogram; - } else { - // We already have one histogram with this name. - DCHECK_EQ(StringPiece(histogram->histogram_name()), - StringPiece(it->second->histogram_name())) - << "hash collision"; - histogram_to_return = it->second; - histogram_to_delete = histogram; - } + // Declared before |auto_lock| to ensure correct destruction order. + std::unique_ptr<HistogramBase> histogram_deleter; + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + + const char* const name = histogram->histogram_name(); + HistogramBase*& registered = top_->histograms_[name]; + + if (!registered) { + // |name| is guaranteed to never change or be deallocated so long + // as the histogram is alive (which is forever). + registered = histogram; + ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322 + // If there are callbacks for this histogram, we set the kCallbackExists + // flag. + const auto callback_iterator = top_->callbacks_.find(name); + if (callback_iterator != top_->callbacks_.end()) { + if (!callback_iterator->second.is_null()) + histogram->SetFlags(HistogramBase::kCallbackExists); + else + histogram->ClearFlags(HistogramBase::kCallbackExists); } + return histogram; + } + + if (histogram == registered) { + // The histogram was registered before. + return histogram; } - delete histogram_to_delete; - return histogram_to_return; + + // We already have one histogram with this name. + histogram_deleter.reset(histogram); + return registered; } // static const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( const BucketRanges* ranges) { DCHECK(ranges->HasValidChecksum()); + + // Declared before |auto_lock| to ensure correct destruction order. std::unique_ptr<const BucketRanges> ranges_deleter; + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); - base::AutoLock auto_lock(lock_.Get()); - if (!ranges_) { + const BucketRanges* const registered = *top_->ranges_.insert(ranges).first; + if (registered == ranges) { ANNOTATE_LEAKING_OBJECT_PTR(ranges); - return ranges; - } - - std::list<const BucketRanges*>* checksum_matching_list; - RangesMap::iterator ranges_it = ranges_->find(ranges->checksum()); - if (ranges_->end() == ranges_it) { - // Add a new matching list to map. - checksum_matching_list = new std::list<const BucketRanges*>(); - ANNOTATE_LEAKING_OBJECT_PTR(checksum_matching_list); - (*ranges_)[ranges->checksum()] = checksum_matching_list; } else { - checksum_matching_list = ranges_it->second; + ranges_deleter.reset(ranges); } - for (const BucketRanges* existing_ranges : *checksum_matching_list) { - if (existing_ranges->Equals(ranges)) { - if (existing_ranges == ranges) { - return ranges; - } else { - ranges_deleter.reset(ranges); - return existing_ranges; - } - } - } - // We haven't found a BucketRanges which has the same ranges. Register the - // new BucketRanges. - checksum_matching_list->push_front(ranges); - return ranges; + return registered; } // static void StatisticsRecorder::WriteHTMLGraph(const std::string& query, std::string* output) { - if (!IsActive()) - return; - - Histograms snapshot; - GetSnapshot(query, &snapshot); - std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); - for (const HistogramBase* histogram : snapshot) { + for (const HistogramBase* const histogram : GetSnapshot(query)) { histogram->WriteHTMLGraph(output); - output->append("<br><hr><br>"); + *output += "<br><hr><br>"; } } // static void StatisticsRecorder::WriteGraph(const std::string& query, std::string* output) { - if (!IsActive()) - return; if (query.length()) StringAppendF(output, "Collections of histograms for %s\n", query.c_str()); else output->append("Collections of all histograms\n"); - Histograms snapshot; - GetSnapshot(query, &snapshot); - std::sort(snapshot.begin(), snapshot.end(), &HistogramNameLesser); - for (const HistogramBase* histogram : snapshot) { + for (const HistogramBase* const histogram : GetSnapshot(query)) { histogram->WriteAscii(output); output->append("\n"); } @@ -205,19 +159,11 @@ void StatisticsRecorder::WriteGraph(const std::string& query, // static std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) { - if (!IsActive()) - return std::string(); - - std::string output("{"); - Histograms snapshot; - GetSnapshot(std::string(), &snapshot); - output += "\"histograms\":["; - bool first_histogram = true; - for (const HistogramBase* histogram : snapshot) { - if (first_histogram) - first_histogram = false; - else - output += ","; + std::string output = "{\"histograms\":["; + const char* sep = ""; + for (const HistogramBase* const histogram : GetHistograms()) { + output += sep; + sep = ","; std::string json; histogram->WriteJSON(&json, verbosity_level); output += json; @@ -227,28 +173,13 @@ std::string StatisticsRecorder::ToJSON(JSONVerbosityLevel verbosity_level) { } // static -void StatisticsRecorder::GetHistograms(Histograms* output) { - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return; - - for (const auto& entry : *histograms_) { - output->push_back(entry.second); - } -} - -// static -void StatisticsRecorder::GetBucketRanges( - std::vector<const BucketRanges*>* output) { - base::AutoLock auto_lock(lock_.Get()); - if (!ranges_) - return; - - for (const auto& entry : *ranges_) { - for (auto* range_entry : *entry.second) { - output->push_back(range_entry); - } - } +std::vector<const BucketRanges*> StatisticsRecorder::GetBucketRanges() { + std::vector<const BucketRanges*> out; + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + out.reserve(top_->ranges_.size()); + out.assign(top_->ranges_.begin(), top_->ranges_.end()); + return out; } // static @@ -258,23 +189,25 @@ HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) { // will acquire the lock at that time. ImportGlobalPersistentHistograms(); - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return nullptr; + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); - HistogramMap::iterator it = histograms_->find(name); - if (histograms_->end() == it) - return nullptr; - return it->second; + const HistogramMap::const_iterator it = top_->histograms_.find(name); + return it != top_->histograms_.end() ? it->second : nullptr; } // static -void StatisticsRecorder::ImportProvidedHistograms() { - if (!providers_) - return; +StatisticsRecorder::HistogramProviders +StatisticsRecorder::GetHistogramProviders() { + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + return top_->providers_; +} +// static +void StatisticsRecorder::ImportProvidedHistograms() { // Merge histogram data from each provider in turn. - for (const WeakPtr<HistogramProvider>& provider : *providers_) { + for (const WeakPtr<HistogramProvider>& provider : GetHistogramProviders()) { // Weak-pointer may be invalid if the provider was destructed, though they // generally never are. if (provider) @@ -288,42 +221,14 @@ void StatisticsRecorder::PrepareDeltas( HistogramBase::Flags flags_to_set, HistogramBase::Flags required_flags, HistogramSnapshotManager* snapshot_manager) { - if (include_persistent) - ImportGlobalPersistentHistograms(); - - auto known = GetKnownHistograms(include_persistent); - snapshot_manager->PrepareDeltas(known.begin(), known.end(), flags_to_set, - required_flags); + snapshot_manager->PrepareDeltas(GetKnownHistograms(include_persistent), + flags_to_set, required_flags); } // static void StatisticsRecorder::InitLogOnShutdown() { - if (!histograms_) - return; - - base::AutoLock auto_lock(lock_.Get()); - g_statistics_recorder_.Get().InitLogOnShutdownWithoutLock(); -} - -// static -void StatisticsRecorder::GetSnapshot(const std::string& query, - Histograms* snapshot) { - // This must be called *before* the lock is acquired below because it will - // call back into this object to register histograms. Those called methods - // will acquire the lock at that time. - ImportGlobalPersistentHistograms(); - - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return; - - // Need a c-string query for comparisons against c-string histogram name. - const char* query_string = query.c_str(); - - for (const auto& entry : *histograms_) { - if (strstr(entry.second->histogram_name(), query_string) != nullptr) - snapshot->push_back(entry.second); - } + const AutoLock auto_lock(lock_.Get()); + InitLogOnShutdownWhileLocked(); } // static @@ -331,16 +236,14 @@ bool StatisticsRecorder::SetCallback( const std::string& name, const StatisticsRecorder::OnSampleCallback& cb) { DCHECK(!cb.is_null()); - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return false; + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); - if (ContainsKey(*callbacks_, name)) + if (!top_->callbacks_.insert({name, cb}).second) return false; - callbacks_->insert(std::make_pair(name, cb)); - auto it = histograms_->find(name); - if (it != histograms_->end()) + const HistogramMap::const_iterator it = top_->histograms_.find(name); + if (it != top_->histograms_.end()) it->second->SetFlags(HistogramBase::kCallbackExists); return true; @@ -348,199 +251,162 @@ bool StatisticsRecorder::SetCallback( // static void StatisticsRecorder::ClearCallback(const std::string& name) { - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return; + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); - callbacks_->erase(name); + top_->callbacks_.erase(name); // We also clear the flag from the histogram (if it exists). - auto it = histograms_->find(name); - if (it != histograms_->end()) + const HistogramMap::const_iterator it = top_->histograms_.find(name); + if (it != top_->histograms_.end()) it->second->ClearFlags(HistogramBase::kCallbackExists); } // static StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( const std::string& name) { - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return OnSampleCallback(); - - auto callback_iterator = callbacks_->find(name); - return callback_iterator != callbacks_->end() ? callback_iterator->second - : OnSampleCallback(); + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + const auto it = top_->callbacks_.find(name); + return it != top_->callbacks_.end() ? it->second : OnSampleCallback(); } // static size_t StatisticsRecorder::GetHistogramCount() { - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_) - return 0; - return histograms_->size(); + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + return top_->histograms_.size(); } // static void StatisticsRecorder::ForgetHistogramForTesting(base::StringPiece name) { - if (!histograms_) - return; + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); - HistogramMap::iterator found = histograms_->find(name); - if (found == histograms_->end()) + const HistogramMap::iterator found = top_->histograms_.find(name); + if (found == top_->histograms_.end()) return; - HistogramBase* base = found->second; + HistogramBase* const base = found->second; if (base->GetHistogramType() != SPARSE_HISTOGRAM) { // When forgetting a histogram, it's likely that other information is // also becoming invalid. Clear the persistent reference that may no // longer be valid. There's no danger in this as, at worst, duplicates // will be created in persistent memory. - Histogram* histogram = static_cast<Histogram*>(base); - histogram->bucket_ranges()->set_persistent_reference(0); + static_cast<Histogram*>(base)->bucket_ranges()->set_persistent_reference(0); } - histograms_->erase(found); + top_->histograms_.erase(found); } // static std::unique_ptr<StatisticsRecorder> StatisticsRecorder::CreateTemporaryForTesting() { + const AutoLock auto_lock(lock_.Get()); return WrapUnique(new StatisticsRecorder()); } // static -void StatisticsRecorder::UninitializeForTesting() { - // Stop now if it's never been initialized. - if (!histograms_) - return; - - // Get the global instance and destruct it. It's held in static memory so - // can't "delete" it; call the destructor explicitly. - DCHECK(g_statistics_recorder_.private_instance_); - g_statistics_recorder_.Get().~StatisticsRecorder(); - - // Now the ugly part. There's no official way to release a LazyInstance once - // created so it's necessary to clear out an internal variable which - // shouldn't be publicly visible but is for initialization reasons. - g_statistics_recorder_.private_instance_ = 0; -} - -// static void StatisticsRecorder::SetRecordChecker( std::unique_ptr<RecordHistogramChecker> record_checker) { - record_checker_ = record_checker.release(); + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + top_->record_checker_ = std::move(record_checker); } // static bool StatisticsRecorder::ShouldRecordHistogram(uint64_t histogram_hash) { - return !record_checker_ || record_checker_->ShouldRecord(histogram_hash); + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + return !top_->record_checker_ || + top_->record_checker_->ShouldRecord(histogram_hash); } // static -std::vector<HistogramBase*> StatisticsRecorder::GetKnownHistograms( - bool include_persistent) { - std::vector<HistogramBase*> known; - base::AutoLock auto_lock(lock_.Get()); - if (!histograms_ || histograms_->empty()) - return known; - - known.reserve(histograms_->size()); - for (const auto& h : *histograms_) { - if (!include_persistent && - (h.second->flags() & HistogramBase::kIsPersistent)) { - continue; +template <typename Predicate> +StatisticsRecorder::Histograms StatisticsRecorder::GetHistogramsWithPredicate( + const Predicate predicate) { + // This must be called *before* the lock is acquired below because it will + // call back into this object to register histograms. Those called methods + // will acquire the lock at that time. + ImportGlobalPersistentHistograms(); + + Histograms out; + + { + const AutoLock auto_lock(lock_.Get()); + EnsureGlobalRecorderWhileLocked(); + out.reserve(top_->histograms_.size()); + for (const auto& entry : top_->histograms_) { + const HistogramBase* const histogram = entry.second; + DCHECK(histogram); + if (predicate(*histogram)) + out.push_back(entry.second); } - known.push_back(h.second); } - return known; + std::sort(out.begin(), out.end(), &HistogramNameLesser); + return out; } // static -void StatisticsRecorder::ImportGlobalPersistentHistograms() { - if (!histograms_) - return; - - // Import histograms from known persistent storage. Histograms could have - // been added by other processes and they must be fetched and recognized - // locally. If the persistent memory segment is not shared between processes, - // this call does nothing. - GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get(); - if (allocator) - allocator->ImportHistogramsToStatisticsRecorder(); +StatisticsRecorder::Histograms StatisticsRecorder::GetHistograms() { + return GetHistogramsWithPredicate([](const HistogramBase&) { return true; }); } -// This singleton instance should be started during the single threaded portion -// of main(), and hence it is not thread safe. It initializes globals to -// provide support for all future calls. -StatisticsRecorder::StatisticsRecorder() { - base::AutoLock auto_lock(lock_.Get()); - - existing_histograms_.reset(histograms_); - existing_callbacks_.reset(callbacks_); - existing_ranges_.reset(ranges_); - existing_providers_.reset(providers_); - existing_record_checker_.reset(record_checker_); - - histograms_ = new HistogramMap; - callbacks_ = new CallbackMap; - ranges_ = new RangesMap; - providers_ = new HistogramProviders; - record_checker_ = nullptr; - - InitLogOnShutdownWithoutLock(); -} - -void StatisticsRecorder::InitLogOnShutdownWithoutLock() { - if (!vlog_initialized_ && VLOG_IS_ON(1)) { - vlog_initialized_ = true; - AtExitManager::RegisterCallback(&DumpHistogramsToVlog, this); - } +// static +StatisticsRecorder::Histograms StatisticsRecorder::GetKnownHistograms( + bool include_persistent) { + return GetHistogramsWithPredicate( + [include_persistent](const HistogramBase& histogram) { + return include_persistent || + (histogram.flags() & HistogramBase::kIsPersistent) == 0; + }); } // static -void StatisticsRecorder::Reset() { - std::unique_ptr<HistogramMap> histograms_deleter; - std::unique_ptr<CallbackMap> callbacks_deleter; - std::unique_ptr<RangesMap> ranges_deleter; - std::unique_ptr<HistogramProviders> providers_deleter; - std::unique_ptr<RecordHistogramChecker> record_checker_deleter; - { - base::AutoLock auto_lock(lock_.Get()); - histograms_deleter.reset(histograms_); - callbacks_deleter.reset(callbacks_); - ranges_deleter.reset(ranges_); - providers_deleter.reset(providers_); - record_checker_deleter.reset(record_checker_); - histograms_ = nullptr; - callbacks_ = nullptr; - ranges_ = nullptr; - providers_ = nullptr; - record_checker_ = nullptr; - } - // We are going to leak the histograms and the ranges. +StatisticsRecorder::Histograms StatisticsRecorder::GetSnapshot( + const std::string& query) { + // Need a C-string query for comparisons against C-string histogram name. + const char* const query_string = query.c_str(); + return GetHistogramsWithPredicate( + [query_string](const HistogramBase& histogram) { + return strstr(histogram.histogram_name(), query_string) != nullptr; + }); } // static -void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { - std::string output; - StatisticsRecorder::WriteGraph(std::string(), &output); - VLOG(1) << output; +void StatisticsRecorder::ImportGlobalPersistentHistograms() { + // Import histograms from known persistent storage. Histograms could have been + // added by other processes and they must be fetched and recognized locally. + // If the persistent memory segment is not shared between processes, this call + // does nothing. + if (GlobalHistogramAllocator* allocator = GlobalHistogramAllocator::Get()) + allocator->ImportHistogramsToStatisticsRecorder(); } - -// static -StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr; -// static -StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = nullptr; -// static -StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr; -// static -StatisticsRecorder::HistogramProviders* StatisticsRecorder::providers_; -// static -RecordHistogramChecker* StatisticsRecorder::record_checker_ = nullptr; -// static -base::LazyInstance<base::Lock>::Leaky StatisticsRecorder::lock_ = - LAZY_INSTANCE_INITIALIZER; +// This singleton instance should be started during the single threaded portion +// of main(), and hence it is not thread safe. It initializes globals to provide +// support for all future calls. +StatisticsRecorder::StatisticsRecorder() { + lock_.Get().AssertAcquired(); + previous_ = top_; + top_ = this; + InitLogOnShutdownWhileLocked(); +} + +// static +void StatisticsRecorder::InitLogOnShutdownWhileLocked() { + lock_.Get().AssertAcquired(); + if (!is_vlog_initialized_ && VLOG_IS_ON(1)) { + is_vlog_initialized_ = true; + const auto dump_to_vlog = [](void*) { + std::string output; + WriteGraph("", &output); + VLOG(1) << output; + }; + AtExitManager::RegisterCallback(dump_to_vlog, nullptr); + } +} } // namespace base diff --git a/chromium/base/metrics/statistics_recorder.h b/chromium/base/metrics/statistics_recorder.h index 49ccaf51917..c994b7e257d 100644 --- a/chromium/base/metrics/statistics_recorder.h +++ b/chromium/base/metrics/statistics_recorder.h @@ -12,10 +12,10 @@ #include <stdint.h> -#include <list> -#include <map> #include <memory> #include <string> +#include <unordered_map> +#include <unordered_set> #include <vector> #include "base/base_export.h" @@ -34,37 +34,19 @@ namespace base { class BucketRanges; class HistogramSnapshotManager; +// In-memory recorder of usage statistics (aka metrics, aka histograms). +// +// All the public methods are static and act on a global recorder. This global +// recorder is internally synchronized and all the static methods are thread +// safe. +// +// StatisticsRecorder doesn't have any public constructor. For testing purpose, +// you can create a temporary recorder using the factory method +// CreateTemporaryForTesting(). This temporary recorder becomes the global one +// until deleted. When this temporary recorder is deleted, it restores the +// previous global one. class BASE_EXPORT StatisticsRecorder { public: - // A class used as a key for the histogram map below. It always references - // a string owned outside of this class, likely in the value of the map. - class StringKey : public StringPiece { - public: - // Constructs the StringKey using various sources. The source must live - // at least as long as the created object. - StringKey(const std::string& str) : StringPiece(str) {} - StringKey(StringPiece str) : StringPiece(str) {} - - // Though StringPiece is better passed by value than by reference, in - // this case it's being passed many times and likely already been stored - // in memory (not just registers) so the benefit of pass-by-value is - // negated. - bool operator<(const StringKey& rhs) const { - // Since order is unimportant in the map and string comparisons can be - // slow, use the length as the primary sort value. - if (length() < rhs.length()) - return true; - if (length() > rhs.length()) - return false; - - // Fall back to an actual string comparison. The lengths are the same - // so a simple memory-compare is sufficient. This is slightly more - // efficient than calling operator<() for StringPiece which would - // again have to check lengths before calling wordmemcmp(). - return wordmemcmp(data(), rhs.data(), length()) < 0; - } - }; - // An interface class that allows the StatisticsRecorder to forcibly merge // histograms from providers when necessary. class HistogramProvider { @@ -73,58 +55,82 @@ class BASE_EXPORT StatisticsRecorder { virtual void MergeHistogramDeltas() = 0; }; - typedef std::map<StringKey, HistogramBase*> HistogramMap; typedef std::vector<HistogramBase*> Histograms; - typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders; + // Restores the previous global recorder. + // + // When several temporary recorders are created using + // CreateTemporaryForTesting(), these recorders must be deleted in reverse + // order of creation. + // + // This method is thread safe. + // + // Precondition: The recorder being deleted is the current global recorder. ~StatisticsRecorder(); - // Initializes the StatisticsRecorder system. Safe to call multiple times. - static void Initialize(); - - // Find out if histograms can now be registered into our list. - static bool IsActive(); - - // Register a provider of histograms that can be called to merge those into - // the global StatisticsRecorder. Calls to ImportProvidedHistograms() will - // fetch from registered providers. + // Registers a provider of histograms that can be called to merge those into + // the global recorder. Calls to ImportProvidedHistograms() will fetch from + // registered providers. + // + // This method is thread safe. static void RegisterHistogramProvider( const WeakPtr<HistogramProvider>& provider); - // Register, or add a new histogram to the collection of statistics. If an + // Registers or adds a new histogram to the collection of statistics. If an // identically named histogram is already registered, then the argument - // |histogram| will deleted. The returned value is always the registered + // |histogram| will be deleted. The returned value is always the registered // histogram (either the argument, or the pre-existing registered histogram). + // + // This method is thread safe. static HistogramBase* RegisterOrDeleteDuplicate(HistogramBase* histogram); - // Register, or add a new BucketRanges. If an identically BucketRanges is - // already registered, then the argument |ranges| will deleted. The returned - // value is always the registered BucketRanges (either the argument, or the - // pre-existing one). + // Registers or adds a new BucketRanges. If an equivalent BucketRanges is + // already registered, then the argument |ranges| will be deleted. The + // returned value is always the registered BucketRanges (either the argument, + // or the pre-existing one). + // + // This method is thread safe. static const BucketRanges* RegisterOrDeleteDuplicateRanges( const BucketRanges* ranges); // Methods for appending histogram data to a string. Only histograms which // have |query| as a substring are written to |output| (an empty string will // process all registered histograms). + // + // These methods are thread safe. static void WriteHTMLGraph(const std::string& query, std::string* output); static void WriteGraph(const std::string& query, std::string* output); // Returns the histograms with |verbosity_level| as the serialization // verbosity. + // + // This method is thread safe. static std::string ToJSON(JSONVerbosityLevel verbosity_level); - // Method for extracting histograms which were marked for use by UMA. - static void GetHistograms(Histograms* output); - - // Method for extracting BucketRanges used by all histograms registered. - static void GetBucketRanges(std::vector<const BucketRanges*>* output); - - // Find a histogram by name. It matches the exact name. This method is thread - // safe. It returns NULL if a matching histogram is not found. + // Gets existing histograms. + // + // The returned histograms are sorted by name. + // + // Ownership of the individual histograms remains with the StatisticsRecorder. + // + // This method is thread safe. + static Histograms GetHistograms(); + + // Gets BucketRanges used by all histograms registered. The order of returned + // BucketRanges is not guaranteed. + // + // This method is thread safe. + static std::vector<const BucketRanges*> GetBucketRanges(); + + // Finds a histogram by name. Matches the exact name. Returns a null pointer + // if a matching histogram is not found. + // + // This method is thread safe. static HistogramBase* FindHistogram(base::StringPiece name); - // Imports histograms from providers. This must be called on the UI thread. + // Imports histograms from providers. + // + // This method must be called on the UI thread. static void ImportProvidedHistograms(); // Snapshots all histograms via |snapshot_manager|. |flags_to_set| is used to @@ -137,128 +143,180 @@ class BASE_EXPORT StatisticsRecorder { HistogramBase::Flags required_flags, HistogramSnapshotManager* snapshot_manager); - // GetSnapshot copies some of the pointers to registered histograms into the - // caller supplied vector (Histograms). Only histograms which have |query| as - // a substring are copied (an empty string will process all registered - // histograms). - static void GetSnapshot(const std::string& query, Histograms* snapshot); + // Gets registered histograms. Only histograms which have |query| as a + // substring in their name are extracted. An empty query returns all + // registered histograms. + // + // The returned histograms are sorted by name. + // + // Ownership of the individual histograms remains with the StatisticsRecorder. + // + // This method is thread safe. + static Histograms GetSnapshot(const std::string& query); typedef base::Callback<void(HistogramBase::Sample)> OnSampleCallback; - // SetCallback sets the callback to notify when a new sample is recorded on - // the histogram referred to by |histogram_name|. The call to this method can - // be be done before or after the histogram is created. This method is thread - // safe. The return value is whether or not the callback was successfully set. + // Sets the callback to notify when a new sample is recorded on the histogram + // referred to by |histogram_name|. Can be called before or after the + // histogram is created. Returns whether the callback was successfully set. + // + // This method is thread safe. static bool SetCallback(const std::string& histogram_name, const OnSampleCallback& callback); - // ClearCallback clears any callback set on the histogram referred to by - // |histogram_name|. This method is thread safe. + // Clears any callback set on the histogram referred to by |histogram_name|. + // + // This method is thread safe. static void ClearCallback(const std::string& histogram_name); - // FindCallback retrieves the callback for the histogram referred to by - // |histogram_name|, or a null callback if no callback exists for this - // histogram. This method is thread safe. + // Retrieves the callback for the histogram referred to by |histogram_name|, + // or a null callback if no callback exists for this histogram. + // + // This method is thread safe. static OnSampleCallback FindCallback(const std::string& histogram_name); // Returns the number of known histograms. + // + // This method is thread safe. static size_t GetHistogramCount(); // Initializes logging histograms with --v=1. Safe to call multiple times. // Is called from ctor but for browser it seems that it is more useful to // start logging after statistics recorder, so we need to init log-on-shutdown // later. + // + // This method is thread safe. static void InitLogOnShutdown(); // Removes a histogram from the internal set of known ones. This can be // necessary during testing persistent histograms where the underlying // memory is being released. + // + // This method is thread safe. static void ForgetHistogramForTesting(base::StringPiece name); - // Creates a local StatisticsRecorder object for testing purposes. All new - // histograms will be registered in it until it is destructed or pushed - // aside for the lifetime of yet another SR object. The destruction of the - // returned object will re-activate the previous one. Always release SR - // objects in the opposite order to which they're created. + // Creates a temporary StatisticsRecorder object for testing purposes. All new + // histograms will be registered in it until it is destructed or pushed aside + // for the lifetime of yet another StatisticsRecorder object. The destruction + // of the returned object will re-activate the previous one. + // StatisticsRecorder objects must be deleted in the opposite order to which + // they're created. + // + // This method is thread safe. static std::unique_ptr<StatisticsRecorder> CreateTemporaryForTesting() WARN_UNUSED_RESULT; - // Resets any global instance of the statistics-recorder that was created - // by a call to Initialize(). - static void UninitializeForTesting(); - // Sets the record checker for determining if a histogram should be recorded. // Record checker doesn't affect any already recorded histograms, so this // method must be called very early, before any threads have started. // Record checker methods can be called on any thread, so they shouldn't // mutate any state. + // // TODO(iburak): This is not yet hooked up to histogram recording // infrastructure. static void SetRecordChecker( std::unique_ptr<RecordHistogramChecker> record_checker); - // Returns true iff the given histogram should be recorded based on - // the ShouldRecord() method of the record checker. - // If the record checker is not set, returns true. + // Checks if the given histogram should be recorded based on the + // ShouldRecord() method of the record checker. If the record checker is not + // set, returns true. + // + // This method is thread safe. static bool ShouldRecordHistogram(uint64_t histogram_hash); private: + typedef std::vector<WeakPtr<HistogramProvider>> HistogramProviders; + + typedef std::unordered_map<StringPiece, HistogramBase*, StringPieceHash> + HistogramMap; + // We keep a map of callbacks to histograms, so that as histograms are // created, we can set the callback properly. - typedef std::map<std::string, OnSampleCallback> CallbackMap; + typedef std::unordered_map<std::string, OnSampleCallback> CallbackMap; + + struct BucketRangesHash { + size_t operator()(const BucketRanges* a) const; + }; + + struct BucketRangesEqual { + bool operator()(const BucketRanges* a, const BucketRanges* b) const; + }; - // We keep all |bucket_ranges_| in a map, from checksum to a list of - // |bucket_ranges_|. Checksum is calculated from the |ranges_| in - // |bucket_ranges_|. - typedef std::map<uint32_t, std::list<const BucketRanges*>*> RangesMap; + typedef std:: + unordered_set<const BucketRanges*, BucketRangesHash, BucketRangesEqual> + RangesMap; - friend struct LazyInstanceTraitsBase<StatisticsRecorder>; friend class StatisticsRecorderTest; FRIEND_TEST_ALL_PREFIXES(StatisticsRecorderTest, IterationTest); - // Fetch set of existing histograms. Ownership of the individual histograms - // remains with the StatisticsRecorder. - static std::vector<HistogramBase*> GetKnownHistograms( - bool include_persistent); - - // Imports histograms from global persistent memory. The global lock must - // not be held during this call. + // Initializes the global recorder if it doesn't already exist. Safe to call + // multiple times. + // + // Precondition: The global lock is already acquired. + static void EnsureGlobalRecorderWhileLocked(); + + // Gets existing histograms matching |predicate|. |Predicate| must have the + // signature bool(const HistogramBase&). + // + // The returned histograms are sorted by name. + // + // Ownership of the individual histograms remains with the StatisticsRecorder. + // + // This method is thread safe. + template <typename Predicate> + static Histograms GetHistogramsWithPredicate(Predicate predicate); + + // Gets existing histograms. + // + // The returned histograms are sorted by name. + // + // Ownership of the individual histograms remains with the StatisticsRecorder. + // + // This method is thread safe. + static Histograms GetKnownHistograms(bool include_persistent); + + // Gets histogram providers. + // + // This method is thread safe. + static HistogramProviders GetHistogramProviders(); + + // Imports histograms from global persistent memory. + // + // Precondition: The global lock must not be held during this call. static void ImportGlobalPersistentHistograms(); - // The constructor just initializes static members. Usually client code should - // use Initialize to do this. But in test code, you can friend this class and - // call the constructor to get a clean StatisticsRecorder. + // Constructs a new StatisticsRecorder and sets it as the current global + // recorder. + // + // Precondition: The global lock is already acquired. StatisticsRecorder(); // Initialize implementation but without lock. Caller should guard // StatisticsRecorder by itself if needed (it isn't in unit tests). - void InitLogOnShutdownWithoutLock(); - - // These are copies of everything that existed when the (test) Statistics- - // Recorder was created. The global ones have to be moved aside to create a - // clean environment. - std::unique_ptr<HistogramMap> existing_histograms_; - std::unique_ptr<CallbackMap> existing_callbacks_; - std::unique_ptr<RangesMap> existing_ranges_; - std::unique_ptr<HistogramProviders> existing_providers_; - std::unique_ptr<RecordHistogramChecker> existing_record_checker_; - - bool vlog_initialized_ = false; - - static void Reset(); - static void DumpHistogramsToVlog(void* instance); - - static HistogramMap* histograms_; - static CallbackMap* callbacks_; - static RangesMap* ranges_; - static HistogramProviders* providers_; - static RecordHistogramChecker* record_checker_; - - // Lock protects access to above maps. This is a LazyInstance to avoid races - // when the above methods are used before Initialize(). Previously each method - // would do |if (!lock_) return;| which would race with - // |lock_ = new Lock;| in StatisticsRecorder(). http://crbug.com/672852. - static base::LazyInstance<base::Lock>::Leaky lock_; + // + // Precondition: The global lock is already acquired. + static void InitLogOnShutdownWhileLocked(); + + HistogramMap histograms_; + CallbackMap callbacks_; + RangesMap ranges_; + HistogramProviders providers_; + std::unique_ptr<RecordHistogramChecker> record_checker_; + + // Previous global recorder that existed when this one was created. + StatisticsRecorder* previous_ = nullptr; + + // Global lock for internal synchronization. + static LazyInstance<Lock>::Leaky lock_; + + // Current global recorder. This recorder is used by static methods. When a + // new global recorder is created by CreateTemporaryForTesting(), then the + // previous global recorder is referenced by top_->previous_. + static StatisticsRecorder* top_; + + // Tracks whether InitLogOnShutdownWhileLocked() has registered a logging + // function that will be called when the program finishes. + static bool is_vlog_initialized_; DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder); }; diff --git a/chromium/base/metrics/statistics_recorder_unittest.cc b/chromium/base/metrics/statistics_recorder_unittest.cc index 92d1bba602e..79ab7bf6b13 100644 --- a/chromium/base/metrics/statistics_recorder_unittest.cc +++ b/chromium/base/metrics/statistics_recorder_unittest.cc @@ -20,6 +20,7 @@ #include "base/metrics/record_histogram_checker.h" #include "base/metrics/sparse_histogram.h" #include "base/values.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -30,9 +31,7 @@ class LogStateSaver { public: LogStateSaver() : old_min_log_level_(logging::GetMinLogLevel()) {} - ~LogStateSaver() { - logging::SetMinLogLevel(old_min_log_level_); - } + ~LogStateSaver() { logging::SetMinLogLevel(old_min_log_level_); } private: int old_min_log_level_; @@ -55,6 +54,11 @@ class OddRecordHistogramChecker : public base::RecordHistogramChecker { namespace base { +using testing::ElementsAre; +using testing::IsEmpty; +using testing::SizeIs; +using testing::UnorderedElementsAre; + class StatisticsRecorderTest : public testing::TestWithParam<bool> { protected: const int32_t kAllocatorMemorySize = 64 << 10; // 64 KiB @@ -70,8 +74,8 @@ class StatisticsRecorderTest : public testing::TestWithParam<bool> { // Use persistent memory for histograms if so indicated by test parameter. if (use_persistent_histogram_allocator_) { - GlobalHistogramAllocator::CreateWithLocalMemory( - kAllocatorMemorySize, 0, "StatisticsRecorderTest"); + GlobalHistogramAllocator::CreateWithLocalMemory(kAllocatorMemorySize, 0, + "StatisticsRecorderTest"); } } @@ -82,15 +86,19 @@ class StatisticsRecorderTest : public testing::TestWithParam<bool> { void InitializeStatisticsRecorder() { DCHECK(!statistics_recorder_); - StatisticsRecorder::UninitializeForTesting(); statistics_recorder_ = StatisticsRecorder::CreateTemporaryForTesting(); } + // Deletes the global recorder if there is any. This is used by test + // NotInitialized to ensure a clean global state. void UninitializeStatisticsRecorder() { statistics_recorder_.reset(); - StatisticsRecorder::UninitializeForTesting(); + delete StatisticsRecorder::top_; + DCHECK(!StatisticsRecorder::top_); } + bool HasGlobalRecorder() { return StatisticsRecorder::top_ != nullptr; } + Histogram* CreateHistogram(const char* name, HistogramBase::Sample min, HistogramBase::Sample max, @@ -102,18 +110,13 @@ class StatisticsRecorderTest : public testing::TestWithParam<bool> { return new Histogram(name, min, max, registered_ranges); } - void DeleteHistogram(HistogramBase* histogram) { - delete histogram; - } + void InitLogOnShutdown() { StatisticsRecorder::InitLogOnShutdown(); } - void InitLogOnShutdown() { - DCHECK(statistics_recorder_); - statistics_recorder_->InitLogOnShutdownWithoutLock(); - } + bool IsVLogInitialized() { return StatisticsRecorder::is_vlog_initialized_; } - bool VLogInitialized() { - DCHECK(statistics_recorder_); - return statistics_recorder_->vlog_initialized_; + void ResetVLogInitialized() { + UninitializeStatisticsRecorder(); + StatisticsRecorder::is_vlog_initialized_ = false; } const bool use_persistent_histogram_allocator_; @@ -132,30 +135,25 @@ INSTANTIATE_TEST_CASE_P(Allocator, StatisticsRecorderTest, testing::Bool()); TEST_P(StatisticsRecorderTest, NotInitialized) { UninitializeStatisticsRecorder(); + EXPECT_FALSE(HasGlobalRecorder()); - ASSERT_FALSE(StatisticsRecorder::IsActive()); + HistogramBase* const histogram = + CreateHistogram("TestHistogram", 1, 1000, 10); + EXPECT_EQ(StatisticsRecorder::RegisterOrDeleteDuplicate(histogram), + histogram); + EXPECT_TRUE(HasGlobalRecorder()); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), ElementsAre(histogram)); - StatisticsRecorder::Histograms registered_histograms; - std::vector<const BucketRanges*> registered_ranges; - - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(0u, registered_histograms.size()); - - Histogram* histogram = CreateHistogram("TestHistogram", 1, 1000, 10); - - // When StatisticsRecorder is not initialized, register is a noop. - EXPECT_EQ(histogram, - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram)); - // Manually delete histogram that was not registered. - DeleteHistogram(histogram); + UninitializeStatisticsRecorder(); + EXPECT_FALSE(HasGlobalRecorder()); - // RegisterOrDeleteDuplicateRanges is a no-op. - BucketRanges* ranges = new BucketRanges(3); + BucketRanges* const ranges = new BucketRanges(3); ranges->ResetChecksum(); - EXPECT_EQ(ranges, - StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges)); - StatisticsRecorder::GetBucketRanges(®istered_ranges); - EXPECT_EQ(0u, registered_ranges.size()); + EXPECT_EQ(StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges), + ranges); + EXPECT_TRUE(HasGlobalRecorder()); + EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), + UnorderedElementsAre(ranges)); } TEST_P(StatisticsRecorderTest, RegisterBucketRanges) { @@ -171,15 +169,15 @@ TEST_P(StatisticsRecorderTest, RegisterBucketRanges) { StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges1)); EXPECT_EQ(ranges2, StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges2)); - StatisticsRecorder::GetBucketRanges(®istered_ranges); - ASSERT_EQ(2u, registered_ranges.size()); + EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), + UnorderedElementsAre(ranges1, ranges2)); // Register some ranges again. EXPECT_EQ(ranges1, StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges1)); - registered_ranges.clear(); - StatisticsRecorder::GetBucketRanges(®istered_ranges); - ASSERT_EQ(2u, registered_ranges.size()); + EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), + UnorderedElementsAre(ranges1, ranges2)); + // Make sure the ranges is still the one we know. ASSERT_EQ(3u, ranges1->size()); EXPECT_EQ(0, ranges1->range(0)); @@ -191,31 +189,40 @@ TEST_P(StatisticsRecorderTest, RegisterBucketRanges) { ranges3->ResetChecksum(); EXPECT_EQ(ranges1, // returning ranges1 StatisticsRecorder::RegisterOrDeleteDuplicateRanges(ranges3)); - registered_ranges.clear(); - StatisticsRecorder::GetBucketRanges(®istered_ranges); - ASSERT_EQ(2u, registered_ranges.size()); + EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), + UnorderedElementsAre(ranges1, ranges2)); } TEST_P(StatisticsRecorderTest, RegisterHistogram) { // Create a Histogram that was not registered. - Histogram* histogram = CreateHistogram("TestHistogram", 1, 1000, 10); + Histogram* const histogram1 = CreateHistogram("TestHistogram1", 1, 1000, 10); - StatisticsRecorder::Histograms registered_histograms; - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(0u, registered_histograms.size()); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), IsEmpty()); // Register the Histogram. - EXPECT_EQ(histogram, - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram)); - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(1u, registered_histograms.size()); + EXPECT_EQ(histogram1, + StatisticsRecorder::RegisterOrDeleteDuplicate(histogram1)); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), ElementsAre(histogram1)); // Register the same Histogram again. - EXPECT_EQ(histogram, - StatisticsRecorder::RegisterOrDeleteDuplicate(histogram)); - registered_histograms.clear(); - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(1u, registered_histograms.size()); + EXPECT_EQ(histogram1, + StatisticsRecorder::RegisterOrDeleteDuplicate(histogram1)); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), ElementsAre(histogram1)); + + // Register another Histogram with the same name. + Histogram* const histogram2 = CreateHistogram("TestHistogram1", 1, 1000, 10); + EXPECT_NE(histogram1, histogram2); + EXPECT_EQ(histogram1, + StatisticsRecorder::RegisterOrDeleteDuplicate(histogram2)); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), ElementsAre(histogram1)); + + // Register another Histogram with a different name. + Histogram* const histogram3 = CreateHistogram("TestHistogram0", 1, 1000, 10); + EXPECT_NE(histogram1, histogram3); + EXPECT_EQ(histogram3, + StatisticsRecorder::RegisterOrDeleteDuplicate(histogram3)); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), + ElementsAre(histogram3, histogram1)); } TEST_P(StatisticsRecorderTest, FindHistogram) { @@ -257,63 +264,45 @@ TEST_P(StatisticsRecorderTest, GetSnapshot) { Histogram::FactoryGet("TestHistogram2", 1, 1000, 10, Histogram::kNoFlags); Histogram::FactoryGet("TestHistogram3", 1, 1000, 10, Histogram::kNoFlags); - StatisticsRecorder::Histograms snapshot; - StatisticsRecorder::GetSnapshot("Test", &snapshot); - EXPECT_EQ(3u, snapshot.size()); - - snapshot.clear(); - StatisticsRecorder::GetSnapshot("1", &snapshot); - EXPECT_EQ(1u, snapshot.size()); - - snapshot.clear(); - StatisticsRecorder::GetSnapshot("hello", &snapshot); - EXPECT_EQ(0u, snapshot.size()); + EXPECT_THAT(StatisticsRecorder::GetSnapshot("Test"), SizeIs(3)); + EXPECT_THAT(StatisticsRecorder::GetSnapshot("1"), SizeIs(1)); + EXPECT_THAT(StatisticsRecorder::GetSnapshot("hello"), IsEmpty()); } TEST_P(StatisticsRecorderTest, RegisterHistogramWithFactoryGet) { - StatisticsRecorder::Histograms registered_histograms; - - StatisticsRecorder::GetHistograms(®istered_histograms); - ASSERT_EQ(0u, registered_histograms.size()); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), IsEmpty()); // Create a histogram. - HistogramBase* histogram = Histogram::FactoryGet( + HistogramBase* const histogram1 = Histogram::FactoryGet( "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - registered_histograms.clear(); - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(1u, registered_histograms.size()); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), ElementsAre(histogram1)); // Get an existing histogram. - HistogramBase* histogram2 = Histogram::FactoryGet( + HistogramBase* const histogram2 = Histogram::FactoryGet( "TestHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - registered_histograms.clear(); - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(1u, registered_histograms.size()); - EXPECT_EQ(histogram, histogram2); + EXPECT_EQ(histogram1, histogram2); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), ElementsAre(histogram1)); // Create a LinearHistogram. - histogram = LinearHistogram::FactoryGet( + HistogramBase* const histogram3 = LinearHistogram::FactoryGet( "TestLinearHistogram", 1, 1000, 10, HistogramBase::kNoFlags); - registered_histograms.clear(); - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(2u, registered_histograms.size()); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), + ElementsAre(histogram1, histogram3)); // Create a BooleanHistogram. - histogram = BooleanHistogram::FactoryGet( + HistogramBase* const histogram4 = BooleanHistogram::FactoryGet( "TestBooleanHistogram", HistogramBase::kNoFlags); - registered_histograms.clear(); - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(3u, registered_histograms.size()); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), + ElementsAre(histogram4, histogram1, histogram3)); // Create a CustomHistogram. std::vector<int> custom_ranges; custom_ranges.push_back(1); custom_ranges.push_back(5); - histogram = CustomHistogram::FactoryGet( + HistogramBase* const histogram5 = CustomHistogram::FactoryGet( "TestCustomHistogram", custom_ranges, HistogramBase::kNoFlags); - registered_histograms.clear(); - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(4u, registered_histograms.size()); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), + ElementsAre(histogram4, histogram5, histogram1, histogram3)); } TEST_P(StatisticsRecorderTest, RegisterHistogramWithMacros) { @@ -331,35 +320,25 @@ TEST_P(StatisticsRecorderTest, RegisterHistogramWithMacros) { // The histogram we got from macro is the same as from FactoryGet. LOCAL_HISTOGRAM_COUNTS("TestHistogramCounts", 30); - registered_histograms.clear(); - StatisticsRecorder::GetHistograms(®istered_histograms); + registered_histograms = StatisticsRecorder::GetHistograms(); ASSERT_EQ(1u, registered_histograms.size()); EXPECT_EQ(histogram, registered_histograms[0]); LOCAL_HISTOGRAM_TIMES("TestHistogramTimes", TimeDelta::FromDays(1)); LOCAL_HISTOGRAM_ENUMERATION("TestHistogramEnumeration", 20, 200); - registered_histograms.clear(); - StatisticsRecorder::GetHistograms(®istered_histograms); - EXPECT_EQ(3u, registered_histograms.size()); + EXPECT_THAT(StatisticsRecorder::GetHistograms(), SizeIs(3)); } TEST_P(StatisticsRecorderTest, BucketRangesSharing) { - std::vector<const BucketRanges*> ranges; - StatisticsRecorder::GetBucketRanges(&ranges); - EXPECT_EQ(0u, ranges.size()); + EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), IsEmpty()); Histogram::FactoryGet("Histogram", 1, 64, 8, HistogramBase::kNoFlags); Histogram::FactoryGet("Histogram2", 1, 64, 8, HistogramBase::kNoFlags); - - StatisticsRecorder::GetBucketRanges(&ranges); - EXPECT_EQ(1u, ranges.size()); + EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), SizeIs(1)); Histogram::FactoryGet("Histogram3", 1, 64, 16, HistogramBase::kNoFlags); - - ranges.clear(); - StatisticsRecorder::GetBucketRanges(&ranges); - EXPECT_EQ(2u, ranges.size()); + EXPECT_THAT(StatisticsRecorder::GetBucketRanges(), SizeIs(2)); } TEST_P(StatisticsRecorderTest, ToJSON) { @@ -423,9 +402,9 @@ TEST_P(StatisticsRecorderTest, IterationTest) { Histogram::FactoryGet("IterationTest1", 1, 64, 16, HistogramBase::kNoFlags); Histogram::FactoryGet("IterationTest2", 1, 64, 16, HistogramBase::kNoFlags); - EXPECT_EQ(2U, StatisticsRecorder::GetKnownHistograms(true).size()); - EXPECT_EQ(use_persistent_histogram_allocator_ ? 0U : 2U, - StatisticsRecorder::GetKnownHistograms(false).size()); + EXPECT_THAT(StatisticsRecorder::GetKnownHistograms(true), SizeIs(2)); + EXPECT_THAT(StatisticsRecorder::GetKnownHistograms(false), + SizeIs(use_persistent_histogram_allocator_ ? 0 : 2)); // Create a new global allocator using the same memory as the old one. Any // old one is kept around so the memory doesn't get released. @@ -442,11 +421,11 @@ TEST_P(StatisticsRecorderTest, IterationTest) { InitializeStatisticsRecorder(); StatisticsRecorder::ImportGlobalPersistentHistograms(); - EXPECT_EQ(use_persistent_histogram_allocator_ ? 2U : 0U, - StatisticsRecorder::GetKnownHistograms(true).size()); + EXPECT_THAT(StatisticsRecorder::GetKnownHistograms(true), + SizeIs(use_persistent_histogram_allocator_ ? 2 : 0)); StatisticsRecorder::ImportGlobalPersistentHistograms(); - EXPECT_EQ(0U, StatisticsRecorder::GetKnownHistograms(false).size()); + EXPECT_THAT(StatisticsRecorder::GetKnownHistograms(false), IsEmpty()); } namespace { @@ -624,33 +603,33 @@ TEST_P(StatisticsRecorderTest, CallbackUsedBeforeHistogramCreatedTest) { } TEST_P(StatisticsRecorderTest, LogOnShutdownNotInitialized) { - UninitializeStatisticsRecorder(); + ResetVLogInitialized(); logging::SetMinLogLevel(logging::LOG_WARNING); InitializeStatisticsRecorder(); EXPECT_FALSE(VLOG_IS_ON(1)); - EXPECT_FALSE(VLogInitialized()); + EXPECT_FALSE(IsVLogInitialized()); InitLogOnShutdown(); - EXPECT_FALSE(VLogInitialized()); + EXPECT_FALSE(IsVLogInitialized()); } TEST_P(StatisticsRecorderTest, LogOnShutdownInitializedExplicitly) { - UninitializeStatisticsRecorder(); + ResetVLogInitialized(); logging::SetMinLogLevel(logging::LOG_WARNING); InitializeStatisticsRecorder(); EXPECT_FALSE(VLOG_IS_ON(1)); - EXPECT_FALSE(VLogInitialized()); + EXPECT_FALSE(IsVLogInitialized()); logging::SetMinLogLevel(logging::LOG_VERBOSE); EXPECT_TRUE(VLOG_IS_ON(1)); InitLogOnShutdown(); - EXPECT_TRUE(VLogInitialized()); + EXPECT_TRUE(IsVLogInitialized()); } TEST_P(StatisticsRecorderTest, LogOnShutdownInitialized) { - UninitializeStatisticsRecorder(); + ResetVLogInitialized(); logging::SetMinLogLevel(logging::LOG_VERBOSE); InitializeStatisticsRecorder(); EXPECT_TRUE(VLOG_IS_ON(1)); - EXPECT_TRUE(VLogInitialized()); + EXPECT_TRUE(IsVLogInitialized()); } class TestHistogramProvider : public StatisticsRecorder::HistogramProvider { |