Bug 1577937 - Throw a StaticDataMutex around sTelemetry r=janerik,froydnj

It could very well happen that someone calls into a static method of
TelemetryImpl during Telemetry shutdown. There could be a race where sTelemetry
is non-null at the top of a method like CanRecordExtended, but is null by the
time we try to use it.

So let's put a StaticDataMutex around it. This isn't trying to mutex the data
itself. Instead I'm just trying to protect places where the pointer is being
read or changed.

Differential Revision: https://phabricator.services.mozilla.com/D44905

--HG--
extra : moz-landing-system : lando
This commit is contained in:
Chris H-C 2019-09-12 19:58:01 +00:00
Родитель 73a8e158b7
Коммит 6458a36928
1 изменённых файлов: 68 добавлений и 35 удалений

Просмотреть файл

@ -28,6 +28,7 @@
#include "mozilla/Attributes.h" #include "mozilla/Attributes.h"
#include "mozilla/BackgroundHangMonitor.h" #include "mozilla/BackgroundHangMonitor.h"
#include "mozilla/Components.h" #include "mozilla/Components.h"
#include "mozilla/DataMutex.h"
#include "mozilla/DebugOnly.h" #include "mozilla/DebugOnly.h"
#include "mozilla/FStream.h" #include "mozilla/FStream.h"
#include "mozilla/IOInterposer.h" #include "mozilla/IOInterposer.h"
@ -186,7 +187,7 @@ class TelemetryImpl final : public nsITelemetry, public nsIMemoryReporter {
void ReadLateWritesStacks(nsIFile* aProfileDir); void ReadLateWritesStacks(nsIFile* aProfileDir);
static TelemetryImpl* sTelemetry; static StaticDataMutex<TelemetryImpl*> sTelemetry;
AutoHashtable<SlowSQLEntryType> mPrivateSQL; AutoHashtable<SlowSQLEntryType> mPrivateSQL;
AutoHashtable<SlowSQLEntryType> mSanitizedSQL; AutoHashtable<SlowSQLEntryType> mSanitizedSQL;
Mutex mHashMutex; Mutex mHashMutex;
@ -211,7 +212,7 @@ class TelemetryImpl final : public nsITelemetry, public nsIMemoryReporter {
WebrtcTelemetry mWebrtcTelemetry; WebrtcTelemetry mWebrtcTelemetry;
}; };
TelemetryImpl* TelemetryImpl::sTelemetry = nullptr; StaticDataMutex<TelemetryImpl*> TelemetryImpl::sTelemetry(nullptr, nullptr);
MOZ_DEFINE_MALLOC_SIZE_OF(TelemetryMallocSizeOf) MOZ_DEFINE_MALLOC_SIZE_OF(TelemetryMallocSizeOf)
@ -343,32 +344,39 @@ class nsFetchTelemetryData : public Runnable {
: mozilla::Runnable("nsFetchTelemetryData"), : mozilla::Runnable("nsFetchTelemetryData"),
mShutdownTimeFilename(aShutdownTimeFilename), mShutdownTimeFilename(aShutdownTimeFilename),
mFailedProfileLockFile(aFailedProfileLockFile), mFailedProfileLockFile(aFailedProfileLockFile),
mTelemetry(TelemetryImpl::sTelemetry),
mProfileDir(aProfileDir) {} mProfileDir(aProfileDir) {}
private: private:
PathCharPtr mShutdownTimeFilename; PathCharPtr mShutdownTimeFilename;
nsCOMPtr<nsIFile> mFailedProfileLockFile; nsCOMPtr<nsIFile> mFailedProfileLockFile;
RefPtr<TelemetryImpl> mTelemetry;
nsCOMPtr<nsIFile> mProfileDir; nsCOMPtr<nsIFile> mProfileDir;
public: public:
void MainThread() { void MainThread() {
mTelemetry->mCachedTelemetryData = true; auto lock = TelemetryImpl::sTelemetry.Lock();
for (unsigned int i = 0, n = mTelemetry->mCallbacks.Count(); i < n; ++i) { auto telemetry = lock.ref();
mTelemetry->mCallbacks[i]->Complete(); telemetry->mCachedTelemetryData = true;
for (unsigned int i = 0, n = telemetry->mCallbacks.Count(); i < n; ++i) {
telemetry->mCallbacks[i]->Complete();
} }
mTelemetry->mCallbacks.Clear(); telemetry->mCallbacks.Clear();
} }
NS_IMETHOD Run() override { NS_IMETHOD Run() override {
LoadFailedLockCount(mTelemetry->mFailedLockCount); uint32_t failedLockCount = 0;
mTelemetry->mLastShutdownTime = uint32_t lastShutdownDuration = 0;
ReadLastShutdownDuration(mShutdownTimeFilename); LoadFailedLockCount(failedLockCount);
mTelemetry->ReadLateWritesStacks(mProfileDir); lastShutdownDuration = ReadLastShutdownDuration(mShutdownTimeFilename);
{
auto lock = TelemetryImpl::sTelemetry.Lock();
auto telemetry = lock.ref();
telemetry->mFailedLockCount = failedLockCount;
telemetry->mLastShutdownTime = lastShutdownDuration;
telemetry->ReadLateWritesStacks(mProfileDir);
}
TelemetryScalar::Set(Telemetry::ScalarID::BROWSER_TIMINGS_LAST_SHUTDOWN, TelemetryScalar::Set(Telemetry::ScalarID::BROWSER_TIMINGS_LAST_SHUTDOWN,
mTelemetry->mLastShutdownTime); lastShutdownDuration);
nsCOMPtr<nsIRunnable> e = nsCOMPtr<nsIRunnable> e =
NewRunnableMethod("nsFetchTelemetryData::MainThread", this, NewRunnableMethod("nsFetchTelemetryData::MainThread", this,
@ -1175,9 +1183,12 @@ TelemetryImpl::GetIsOfficialTelemetry(bool* ret) {
} }
already_AddRefed<nsITelemetry> TelemetryImpl::CreateTelemetryInstance() { already_AddRefed<nsITelemetry> TelemetryImpl::CreateTelemetryInstance() {
MOZ_ASSERT( {
sTelemetry == nullptr, auto lock = sTelemetry.Lock();
"CreateTelemetryInstance may only be called once, via GetService()"); MOZ_ASSERT(
*lock == nullptr,
"CreateTelemetryInstance may only be called once, via GetService()");
}
bool useTelemetry = false; bool useTelemetry = false;
#ifndef FUZZING #ifndef FUZZING
@ -1202,17 +1213,21 @@ already_AddRefed<nsITelemetry> TelemetryImpl::CreateTelemetryInstance() {
TelemetryOrigin::InitializeGlobalState(); TelemetryOrigin::InitializeGlobalState();
// Now, create and initialize the Telemetry global state. // Now, create and initialize the Telemetry global state.
sTelemetry = new TelemetryImpl(); TelemetryImpl* telemetry = new TelemetryImpl();
{
auto lock = sTelemetry.Lock();
*lock = telemetry;
}
// AddRef for the local reference // AddRef for the local reference
NS_ADDREF(sTelemetry); NS_ADDREF(telemetry);
// AddRef for the caller // AddRef for the caller
nsCOMPtr<nsITelemetry> ret = sTelemetry; nsCOMPtr<nsITelemetry> ret = telemetry;
sTelemetry->mCanRecordBase = useTelemetry; telemetry->mCanRecordBase = useTelemetry;
sTelemetry->mCanRecordExtended = useTelemetry; telemetry->mCanRecordExtended = useTelemetry;
sTelemetry->InitMemoryReporter(); telemetry->InitMemoryReporter();
InitHistogramRecordingEnabled(); // requires sTelemetry to exist InitHistogramRecordingEnabled(); // requires sTelemetry to exist
#if defined(MOZ_TELEMETRY_GECKOVIEW) #if defined(MOZ_TELEMETRY_GECKOVIEW)
@ -1230,7 +1245,10 @@ already_AddRefed<nsITelemetry> TelemetryImpl::CreateTelemetryInstance() {
void TelemetryImpl::ShutdownTelemetry() { void TelemetryImpl::ShutdownTelemetry() {
// No point in collecting IO beyond this point // No point in collecting IO beyond this point
ClearIOReporting(); ClearIOReporting();
NS_IF_RELEASE(sTelemetry); {
auto lock = sTelemetry.Lock();
NS_IF_RELEASE(lock.ref());
}
// Lastly, de-initialise the TelemetryHistogram and TelemetryScalar global // Lastly, de-initialise the TelemetryHistogram and TelemetryScalar global
// states, so as to release any heap storage that would otherwise be kept // states, so as to release any heap storage that would otherwise be kept
@ -1250,13 +1268,15 @@ void TelemetryImpl::ShutdownTelemetry() {
void TelemetryImpl::StoreSlowSQL(const nsACString& sql, uint32_t delay, void TelemetryImpl::StoreSlowSQL(const nsACString& sql, uint32_t delay,
SanitizedState state) { SanitizedState state) {
auto lock = sTelemetry.Lock();
auto telemetry = lock.ref();
AutoHashtable<SlowSQLEntryType>* slowSQLMap = nullptr; AutoHashtable<SlowSQLEntryType>* slowSQLMap = nullptr;
if (state == Sanitized) if (state == Sanitized)
slowSQLMap = &(sTelemetry->mSanitizedSQL); slowSQLMap = &(telemetry->mSanitizedSQL);
else else
slowSQLMap = &(sTelemetry->mPrivateSQL); slowSQLMap = &(telemetry->mPrivateSQL);
MutexAutoLock hashMutex(sTelemetry->mHashMutex); MutexAutoLock hashMutex(telemetry->mHashMutex);
SlowSQLEntryType* entry = slowSQLMap->GetEntry(sql); SlowSQLEntryType* entry = slowSQLMap->GetEntry(sql);
if (!entry) { if (!entry) {
@ -1455,7 +1475,12 @@ void TelemetryImpl::RecordSlowStatement(const nsACString& sql,
MOZ_ASSERT(!sql.IsEmpty()); MOZ_ASSERT(!sql.IsEmpty());
MOZ_ASSERT(!dbName.IsEmpty()); MOZ_ASSERT(!dbName.IsEmpty());
if (!sTelemetry || !TelemetryHistogram::CanRecordExtended()) return; {
auto lock = sTelemetry.Lock();
if (!lock.ref() || !TelemetryHistogram::CanRecordExtended()) {
return;
}
}
bool recordStatement = false; bool recordStatement = false;
@ -1504,17 +1529,21 @@ void TelemetryImpl::RecordSlowStatement(const nsACString& sql,
void TelemetryImpl::RecordIceCandidates(const uint32_t iceCandidateBitmask, void TelemetryImpl::RecordIceCandidates(const uint32_t iceCandidateBitmask,
const bool success) { const bool success) {
if (!sTelemetry || !TelemetryHistogram::CanRecordExtended()) return; auto lock = sTelemetry.Lock();
auto telemetry = lock.ref();
if (!telemetry || !TelemetryHistogram::CanRecordExtended()) return;
sTelemetry->mWebrtcTelemetry.RecordIceCandidateMask(iceCandidateBitmask, telemetry->mWebrtcTelemetry.RecordIceCandidateMask(iceCandidateBitmask,
success); success);
} }
#if defined(MOZ_GECKO_PROFILER) #if defined(MOZ_GECKO_PROFILER)
void TelemetryImpl::DoStackCapture(const nsACString& aKey) { void TelemetryImpl::DoStackCapture(const nsACString& aKey) {
if (Telemetry::CanRecordExtended() && XRE_IsParentProcess()) { if (Telemetry::CanRecordExtended() && XRE_IsParentProcess()) {
sTelemetry->mStackCapturer.Capture(aKey); auto lock = sTelemetry.Lock();
auto telemetry = lock.ref();
telemetry->mStackCapturer.Capture(aKey);
} }
} }
#endif #endif
@ -1527,20 +1556,24 @@ nsresult TelemetryImpl::CaptureStack(const nsACString& aKey) {
} }
bool TelemetryImpl::CanRecordBase() { bool TelemetryImpl::CanRecordBase() {
if (!sTelemetry) { auto lock = sTelemetry.Lock();
auto telemetry = lock.ref();
if (!telemetry) {
return false; return false;
} }
bool canRecordBase; bool canRecordBase;
nsresult rv = sTelemetry->GetCanRecordBase(&canRecordBase); nsresult rv = telemetry->GetCanRecordBase(&canRecordBase);
return NS_SUCCEEDED(rv) && canRecordBase; return NS_SUCCEEDED(rv) && canRecordBase;
} }
bool TelemetryImpl::CanRecordExtended() { bool TelemetryImpl::CanRecordExtended() {
if (!sTelemetry) { auto lock = sTelemetry.Lock();
auto telemetry = lock.ref();
if (!telemetry) {
return false; return false;
} }
bool canRecordExtended; bool canRecordExtended;
nsresult rv = sTelemetry->GetCanRecordExtended(&canRecordExtended); nsresult rv = telemetry->GetCanRecordExtended(&canRecordExtended);
return NS_SUCCEEDED(rv) && canRecordExtended; return NS_SUCCEEDED(rv) && canRecordExtended;
} }