Commit c435080b authored by gab's avatar gab Committed by Commit bot

Fix TSAN race in access to StatisticsRecorder::lock_

BUG=672852
TEST=No more content_unittests, events_unittests, and extensions_unittests failures on linux_chromium_tsan_rel_ng
Confirmed @ https://build.chromium.org/p/tryserver.chromium.linux/builders/linux_chromium_tsan_rel_ng/builds/377

Review-Url: https://codereview.chromium.org/2565893002
Cr-Commit-Position: refs/heads/master@{#437932}
parent a6ba8d8c
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
#include "base/metrics/persistent_histogram_allocator.h" #include "base/metrics/persistent_histogram_allocator.h"
#include "base/stl_util.h" #include "base/stl_util.h"
#include "base/strings/stringprintf.h" #include "base/strings/stringprintf.h"
#include "base/synchronization/lock.h"
#include "base/values.h" #include "base/values.h"
namespace { namespace {
...@@ -59,10 +58,10 @@ StatisticsRecorder::HistogramIterator::~HistogramIterator() {} ...@@ -59,10 +58,10 @@ StatisticsRecorder::HistogramIterator::~HistogramIterator() {}
StatisticsRecorder::HistogramIterator& StatisticsRecorder::HistogramIterator&
StatisticsRecorder::HistogramIterator::operator++() { StatisticsRecorder::HistogramIterator::operator++() {
const HistogramMap::iterator histograms_end = histograms_->end(); const HistogramMap::iterator histograms_end = histograms_->end();
if (iter_ == histograms_end || lock_ == NULL) if (iter_ == histograms_end)
return *this; return *this;
base::AutoLock auto_lock(*lock_); base::AutoLock auto_lock(lock_.Get());
for (;;) { for (;;) {
++iter_; ++iter_;
...@@ -79,13 +78,12 @@ StatisticsRecorder::HistogramIterator::operator++() { ...@@ -79,13 +78,12 @@ StatisticsRecorder::HistogramIterator::operator++() {
} }
StatisticsRecorder::~StatisticsRecorder() { StatisticsRecorder::~StatisticsRecorder() {
DCHECK(lock_);
DCHECK(histograms_); DCHECK(histograms_);
DCHECK(ranges_); DCHECK(ranges_);
// Clean out what this object created and then restore what existed before. // Clean out what this object created and then restore what existed before.
Reset(); Reset();
base::AutoLock auto_lock(*lock_); base::AutoLock auto_lock(lock_.Get());
histograms_ = existing_histograms_.release(); histograms_ = existing_histograms_.release();
callbacks_ = existing_callbacks_.release(); callbacks_ = existing_callbacks_.release();
ranges_ = existing_ranges_.release(); ranges_ = existing_ranges_.release();
...@@ -110,31 +108,26 @@ void StatisticsRecorder::Initialize() { ...@@ -110,31 +108,26 @@ void StatisticsRecorder::Initialize() {
// static // static
bool StatisticsRecorder::IsActive() { bool StatisticsRecorder::IsActive() {
if (lock_ == NULL) base::AutoLock auto_lock(lock_.Get());
return false; return histograms_ != nullptr;
base::AutoLock auto_lock(*lock_);
return NULL != histograms_;
} }
// static // static
HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate( HistogramBase* StatisticsRecorder::RegisterOrDeleteDuplicate(
HistogramBase* histogram) { HistogramBase* histogram) {
// As per crbug.com/79322 the histograms are intentionally leaked, so we need HistogramBase* histogram_to_delete = nullptr;
// to annotate them. Because ANNOTATE_LEAKING_OBJECT_PTR may be used only once HistogramBase* histogram_to_return = nullptr;
// for an object, the duplicates should not be annotated.
// Callers are responsible for not calling RegisterOrDeleteDuplicate(ptr)
// twice if (lock_ == NULL) || (!histograms_).
if (lock_ == NULL) {
ANNOTATE_LEAKING_OBJECT_PTR(histogram); // see crbug.com/79322
return histogram;
}
HistogramBase* histogram_to_delete = NULL;
HistogramBase* histogram_to_return = NULL;
{ {
base::AutoLock auto_lock(*lock_); base::AutoLock auto_lock(lock_.Get());
if (histograms_ == NULL) { if (!histograms_) {
histogram_to_return = histogram; 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 { } else {
const std::string& name = histogram->histogram_name(); const std::string& name = histogram->histogram_name();
HistogramMap::iterator it = histograms_->find(name); HistogramMap::iterator it = histograms_->find(name);
...@@ -175,13 +168,8 @@ const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges( ...@@ -175,13 +168,8 @@ const BucketRanges* StatisticsRecorder::RegisterOrDeleteDuplicateRanges(
DCHECK(ranges->HasValidChecksum()); DCHECK(ranges->HasValidChecksum());
std::unique_ptr<const BucketRanges> ranges_deleter; std::unique_ptr<const BucketRanges> ranges_deleter;
if (lock_ == NULL) { base::AutoLock auto_lock(lock_.Get());
ANNOTATE_LEAKING_OBJECT_PTR(ranges); if (!ranges_) {
return ranges;
}
base::AutoLock auto_lock(*lock_);
if (ranges_ == NULL) {
ANNOTATE_LEAKING_OBJECT_PTR(ranges); ANNOTATE_LEAKING_OBJECT_PTR(ranges);
return ranges; return ranges;
} }
...@@ -278,10 +266,8 @@ std::string StatisticsRecorder::ToJSON(const std::string& query) { ...@@ -278,10 +266,8 @@ std::string StatisticsRecorder::ToJSON(const std::string& query) {
// static // static
void StatisticsRecorder::GetHistograms(Histograms* output) { void StatisticsRecorder::GetHistograms(Histograms* output) {
if (lock_ == NULL) base::AutoLock auto_lock(lock_.Get());
return; if (!histograms_)
base::AutoLock auto_lock(*lock_);
if (histograms_ == NULL)
return; return;
for (const auto& entry : *histograms_) { for (const auto& entry : *histograms_) {
...@@ -292,10 +278,8 @@ void StatisticsRecorder::GetHistograms(Histograms* output) { ...@@ -292,10 +278,8 @@ void StatisticsRecorder::GetHistograms(Histograms* output) {
// static // static
void StatisticsRecorder::GetBucketRanges( void StatisticsRecorder::GetBucketRanges(
std::vector<const BucketRanges*>* output) { std::vector<const BucketRanges*>* output) {
if (lock_ == NULL) base::AutoLock auto_lock(lock_.Get());
return; if (!ranges_)
base::AutoLock auto_lock(*lock_);
if (ranges_ == NULL)
return; return;
for (const auto& entry : *ranges_) { for (const auto& entry : *ranges_) {
...@@ -312,15 +296,13 @@ HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) { ...@@ -312,15 +296,13 @@ HistogramBase* StatisticsRecorder::FindHistogram(base::StringPiece name) {
// will acquire the lock at that time. // will acquire the lock at that time.
ImportGlobalPersistentHistograms(); ImportGlobalPersistentHistograms();
if (lock_ == NULL) base::AutoLock auto_lock(lock_.Get());
return NULL; if (!histograms_)
base::AutoLock auto_lock(*lock_); return nullptr;
if (histograms_ == NULL)
return NULL;
HistogramMap::iterator it = histograms_->find(name); HistogramMap::iterator it = histograms_->find(name);
if (histograms_->end() == it) if (histograms_->end() == it)
return NULL; return nullptr;
return it->second; return it->second;
} }
...@@ -332,7 +314,7 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::begin( ...@@ -332,7 +314,7 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
HistogramMap::iterator iter_begin; HistogramMap::iterator iter_begin;
{ {
base::AutoLock auto_lock(*lock_); base::AutoLock auto_lock(lock_.Get());
iter_begin = histograms_->begin(); iter_begin = histograms_->begin();
} }
return HistogramIterator(iter_begin, include_persistent); return HistogramIterator(iter_begin, include_persistent);
...@@ -342,7 +324,7 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::begin( ...@@ -342,7 +324,7 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::begin(
StatisticsRecorder::HistogramIterator StatisticsRecorder::end() { StatisticsRecorder::HistogramIterator StatisticsRecorder::end() {
HistogramMap::iterator iter_end; HistogramMap::iterator iter_end;
{ {
base::AutoLock auto_lock(*lock_); base::AutoLock auto_lock(lock_.Get());
iter_end = histograms_->end(); iter_end = histograms_->end();
} }
return HistogramIterator(iter_end, true); return HistogramIterator(iter_end, true);
...@@ -350,19 +332,18 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::end() { ...@@ -350,19 +332,18 @@ StatisticsRecorder::HistogramIterator StatisticsRecorder::end() {
// static // static
void StatisticsRecorder::InitLogOnShutdown() { void StatisticsRecorder::InitLogOnShutdown() {
if (lock_ == nullptr) if (!histograms_)
return; return;
base::AutoLock auto_lock(*lock_);
base::AutoLock auto_lock(lock_.Get());
g_statistics_recorder_.Get().InitLogOnShutdownWithoutLock(); g_statistics_recorder_.Get().InitLogOnShutdownWithoutLock();
} }
// static // static
void StatisticsRecorder::GetSnapshot(const std::string& query, void StatisticsRecorder::GetSnapshot(const std::string& query,
Histograms* snapshot) { Histograms* snapshot) {
if (lock_ == NULL) base::AutoLock auto_lock(lock_.Get());
return; if (!histograms_)
base::AutoLock auto_lock(*lock_);
if (histograms_ == NULL)
return; return;
for (const auto& entry : *histograms_) { for (const auto& entry : *histograms_) {
...@@ -376,10 +357,8 @@ bool StatisticsRecorder::SetCallback( ...@@ -376,10 +357,8 @@ bool StatisticsRecorder::SetCallback(
const std::string& name, const std::string& name,
const StatisticsRecorder::OnSampleCallback& cb) { const StatisticsRecorder::OnSampleCallback& cb) {
DCHECK(!cb.is_null()); DCHECK(!cb.is_null());
if (lock_ == NULL) base::AutoLock auto_lock(lock_.Get());
return false; if (!histograms_)
base::AutoLock auto_lock(*lock_);
if (histograms_ == NULL)
return false; return false;
if (ContainsKey(*callbacks_, name)) if (ContainsKey(*callbacks_, name))
...@@ -395,10 +374,8 @@ bool StatisticsRecorder::SetCallback( ...@@ -395,10 +374,8 @@ bool StatisticsRecorder::SetCallback(
// static // static
void StatisticsRecorder::ClearCallback(const std::string& name) { void StatisticsRecorder::ClearCallback(const std::string& name) {
if (lock_ == NULL) base::AutoLock auto_lock(lock_.Get());
return; if (!histograms_)
base::AutoLock auto_lock(*lock_);
if (histograms_ == NULL)
return; return;
callbacks_->erase(name); callbacks_->erase(name);
...@@ -412,10 +389,8 @@ void StatisticsRecorder::ClearCallback(const std::string& name) { ...@@ -412,10 +389,8 @@ void StatisticsRecorder::ClearCallback(const std::string& name) {
// static // static
StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
const std::string& name) { const std::string& name) {
if (lock_ == NULL) base::AutoLock auto_lock(lock_.Get());
return OnSampleCallback(); if (!histograms_)
base::AutoLock auto_lock(*lock_);
if (histograms_ == NULL)
return OnSampleCallback(); return OnSampleCallback();
auto callback_iterator = callbacks_->find(name); auto callback_iterator = callbacks_->find(name);
...@@ -425,10 +400,7 @@ StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback( ...@@ -425,10 +400,7 @@ StatisticsRecorder::OnSampleCallback StatisticsRecorder::FindCallback(
// static // static
size_t StatisticsRecorder::GetHistogramCount() { size_t StatisticsRecorder::GetHistogramCount() {
if (!lock_) base::AutoLock auto_lock(lock_.Get());
return 0;
base::AutoLock auto_lock(*lock_);
if (!histograms_) if (!histograms_)
return 0; return 0;
return histograms_->size(); return histograms_->size();
...@@ -449,7 +421,7 @@ StatisticsRecorder::CreateTemporaryForTesting() { ...@@ -449,7 +421,7 @@ StatisticsRecorder::CreateTemporaryForTesting() {
// static // static
void StatisticsRecorder::UninitializeForTesting() { void StatisticsRecorder::UninitializeForTesting() {
// Stop now if it's never been initialized. // Stop now if it's never been initialized.
if (lock_ == NULL || histograms_ == NULL) if (!histograms_)
return; return;
// Get the global instance and destruct it. It's held in static memory so // Get the global instance and destruct it. It's held in static memory so
...@@ -465,7 +437,7 @@ void StatisticsRecorder::UninitializeForTesting() { ...@@ -465,7 +437,7 @@ void StatisticsRecorder::UninitializeForTesting() {
// static // static
void StatisticsRecorder::ImportGlobalPersistentHistograms() { void StatisticsRecorder::ImportGlobalPersistentHistograms() {
if (lock_ == NULL) if (!histograms_)
return; return;
// Import histograms from known persistent storage. Histograms could have // Import histograms from known persistent storage. Histograms could have
...@@ -481,17 +453,7 @@ void StatisticsRecorder::ImportGlobalPersistentHistograms() { ...@@ -481,17 +453,7 @@ void StatisticsRecorder::ImportGlobalPersistentHistograms() {
// of main(), and hence it is not thread safe. It initializes globals to // of main(), and hence it is not thread safe. It initializes globals to
// provide support for all future calls. // provide support for all future calls.
StatisticsRecorder::StatisticsRecorder() { StatisticsRecorder::StatisticsRecorder() {
if (lock_ == NULL) { base::AutoLock auto_lock(lock_.Get());
// This will leak on purpose. It's the only way to make sure we won't race
// against the static uninitialization of the module while one of our
// static methods relying on the lock get called at an inappropriate time
// during the termination phase. Since it's a static data member, we will
// leak one per process, which would be similar to the instance allocated
// during static initialization and released only on process termination.
lock_ = new base::Lock;
}
base::AutoLock auto_lock(*lock_);
existing_histograms_.reset(histograms_); existing_histograms_.reset(histograms_);
existing_callbacks_.reset(callbacks_); existing_callbacks_.reset(callbacks_);
...@@ -513,23 +475,18 @@ void StatisticsRecorder::InitLogOnShutdownWithoutLock() { ...@@ -513,23 +475,18 @@ void StatisticsRecorder::InitLogOnShutdownWithoutLock() {
// static // static
void StatisticsRecorder::Reset() { void StatisticsRecorder::Reset() {
// If there's no lock then there is nothing to reset.
if (!lock_)
return;
std::unique_ptr<HistogramMap> histograms_deleter; std::unique_ptr<HistogramMap> histograms_deleter;
std::unique_ptr<CallbackMap> callbacks_deleter; std::unique_ptr<CallbackMap> callbacks_deleter;
std::unique_ptr<RangesMap> ranges_deleter; std::unique_ptr<RangesMap> ranges_deleter;
// We don't delete lock_ on purpose to avoid having to properly protect
// against it going away after we checked for NULL in the static methods.
{ {
base::AutoLock auto_lock(*lock_); base::AutoLock auto_lock(lock_.Get());
histograms_deleter.reset(histograms_); histograms_deleter.reset(histograms_);
callbacks_deleter.reset(callbacks_); callbacks_deleter.reset(callbacks_);
ranges_deleter.reset(ranges_); ranges_deleter.reset(ranges_);
histograms_ = NULL; histograms_ = nullptr;
callbacks_ = NULL; callbacks_ = nullptr;
ranges_ = NULL; ranges_ = nullptr;
} }
// We are going to leak the histograms and the ranges. // We are going to leak the histograms and the ranges.
} }
...@@ -543,12 +500,13 @@ void StatisticsRecorder::DumpHistogramsToVlog(void* instance) { ...@@ -543,12 +500,13 @@ void StatisticsRecorder::DumpHistogramsToVlog(void* instance) {
// static // static
StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = NULL; StatisticsRecorder::HistogramMap* StatisticsRecorder::histograms_ = nullptr;
// static // static
StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = NULL; StatisticsRecorder::CallbackMap* StatisticsRecorder::callbacks_ = nullptr;
// static // static
StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = NULL; StatisticsRecorder::RangesMap* StatisticsRecorder::ranges_ = nullptr;
// static // static
base::Lock* StatisticsRecorder::lock_ = NULL; base::LazyInstance<base::Lock>::Leaky StatisticsRecorder::lock_ =
LAZY_INSTANCE_INITIALIZER;
} // namespace base } // namespace base
...@@ -25,11 +25,11 @@ ...@@ -25,11 +25,11 @@
#include "base/macros.h" #include "base/macros.h"
#include "base/metrics/histogram_base.h" #include "base/metrics/histogram_base.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/synchronization/lock.h"
namespace base { namespace base {
class BucketRanges; class BucketRanges;
class Lock;
class BASE_EXPORT StatisticsRecorder { class BASE_EXPORT StatisticsRecorder {
public: public:
...@@ -230,8 +230,11 @@ class BASE_EXPORT StatisticsRecorder { ...@@ -230,8 +230,11 @@ class BASE_EXPORT StatisticsRecorder {
static CallbackMap* callbacks_; static CallbackMap* callbacks_;
static RangesMap* ranges_; static RangesMap* ranges_;
// Lock protects access to above maps. // Lock protects access to above maps. This is a LazyInstance to avoid races
static base::Lock* lock_; // 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_;
DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder); DISALLOW_COPY_AND_ASSIGN(StatisticsRecorder);
}; };
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment