diff options
Diffstat (limited to 'dom/media/doctor/DecoderDoctorLogger.cpp')
-rw-r--r-- | dom/media/doctor/DecoderDoctorLogger.cpp | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/dom/media/doctor/DecoderDoctorLogger.cpp b/dom/media/doctor/DecoderDoctorLogger.cpp new file mode 100644 index 0000000000..927650babc --- /dev/null +++ b/dom/media/doctor/DecoderDoctorLogger.cpp @@ -0,0 +1,176 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et cindent: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "DecoderDoctorLogger.h" + +#include "DDLogUtils.h" +#include "DDMediaLogs.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/SchedulerGroup.h" +#include "mozilla/Unused.h" + +namespace mozilla { + +/* static */ Atomic<DecoderDoctorLogger::LogState, ReleaseAcquire> + DecoderDoctorLogger::sLogState{DecoderDoctorLogger::scDisabled}; + +/* static */ const char* DecoderDoctorLogger::sShutdownReason = nullptr; + +static DDMediaLogs* sMediaLogs; + +/* static */ +void DecoderDoctorLogger::Init() { + MOZ_ASSERT(static_cast<LogState>(sLogState) == scDisabled); + if (MOZ_LOG_TEST(sDecoderDoctorLoggerLog, LogLevel::Error) || + MOZ_LOG_TEST(sDecoderDoctorLoggerEndLog, LogLevel::Error)) { + EnableLogging(); + } +} + +// First DDLogShutdowner sets sLogState to scShutdown, to prevent further +// logging. +struct DDLogShutdowner { + ~DDLogShutdowner() { + DDL_INFO("Shutting down"); + // Prevent further logging, some may racily seep in, it's fine as the + // logging infrastructure would still be alive until DDLogDeleter runs. + DecoderDoctorLogger::ShutdownLogging(); + } +}; +static UniquePtr<DDLogShutdowner> sDDLogShutdowner; + +// Later DDLogDeleter will delete the message queue and media logs. +struct DDLogDeleter { + ~DDLogDeleter() { + if (sMediaLogs) { + DDL_INFO("Final processing of collected logs"); + delete sMediaLogs; + sMediaLogs = nullptr; + } + } +}; +static UniquePtr<DDLogDeleter> sDDLogDeleter; + +/* static */ +void DecoderDoctorLogger::PanicInternal(const char* aReason, bool aDontBlock) { + for (;;) { + const LogState state = static_cast<LogState>(sLogState); + if (state == scEnabling && !aDontBlock) { + // Wait for the end of the enabling process (unless we're in it, in which + // case we don't want to block.) + continue; + } + if (state == scShutdown) { + // Already shutdown, nothing more to do. + break; + } + if (sLogState.compareExchange(state, scShutdown)) { + // We are the one performing the first shutdown -> Record reason. + sShutdownReason = aReason; + // Free as much memory as possible. + if (sMediaLogs) { + // Shutdown the medialogs processing thread, and free as much memory + // as possible. + sMediaLogs->Panic(); + } + // sMediaLogs and sQueue will be deleted by DDLogDeleter. + // We don't want to delete them right now, because there could be a race + // where another thread started logging or retrieving logs before we + // changed the state to scShutdown, but has been delayed before actually + // trying to write or read log messages, thereby causing a UAF. + } + // If someone else changed the state, we'll just loop around, and either + // shutdown already happened elsewhere, or we'll try to shutdown again. + } +} + +/* static */ +bool DecoderDoctorLogger::EnsureLogIsEnabled() { +#ifdef RELEASE_OR_BETA + // Just refuse to enable DDLogger on release&beta because it makes it too easy + // to trigger an OOM. See bug 1571648. + return false; +#else + for (;;) { + LogState state = static_cast<LogState>(sLogState); + switch (state) { + case scDisabled: + // Currently disabled, try to be the one to enable. + if (sLogState.compareExchange(scDisabled, scEnabling)) { + // We are the one to enable logging, state won't change (except for + // possible shutdown.) + // Create DDMediaLogs singleton, which will process the message queue. + DDMediaLogs::ConstructionResult mediaLogsConstruction = + DDMediaLogs::New(); + if (NS_FAILED(mediaLogsConstruction.mRv)) { + PanicInternal("Failed to enable logging", /* aDontBlock */ true); + return false; + } + MOZ_ASSERT(mediaLogsConstruction.mMediaLogs); + sMediaLogs = mediaLogsConstruction.mMediaLogs; + // Setup shutdown-time clean-up. + MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch( + TaskCategory::Other, + NS_NewRunnableFunction("DDLogger shutdown setup", [] { + sDDLogShutdowner = MakeUnique<DDLogShutdowner>(); + ClearOnShutdown(&sDDLogShutdowner, + ShutdownPhase::XPCOMShutdown); + sDDLogDeleter = MakeUnique<DDLogDeleter>(); + ClearOnShutdown(&sDDLogDeleter, + ShutdownPhase::XPCOMShutdownThreads); + }))); + + // Nobody else should change the state when *we* are enabling logging. + MOZ_ASSERT(sLogState == scEnabling); + sLogState = scEnabled; + DDL_INFO("Logging enabled"); + return true; + } + // Someone else changed the state before our compareExchange, just loop + // around to examine the new situation. + break; + case scEnabled: + return true; + case scEnabling: + // Someone else is currently enabling logging, actively wait by just + // looping, until the state changes. + break; + case scShutdown: + // Shutdown is non-recoverable, we cannot enable logging again. + return false; + } + // Not returned yet, loop around to examine the new situation. + } +#endif +} + +/* static */ +void DecoderDoctorLogger::EnableLogging() { Unused << EnsureLogIsEnabled(); } + +/* static */ RefPtr<DecoderDoctorLogger::LogMessagesPromise> +DecoderDoctorLogger::RetrieveMessages( + const dom::HTMLMediaElement* aMediaElement) { + if (MOZ_UNLIKELY(!EnsureLogIsEnabled())) { + DDL_WARN("Request (for %p) but there are no logs", aMediaElement); + return DecoderDoctorLogger::LogMessagesPromise::CreateAndReject( + NS_ERROR_DOM_MEDIA_ABORT_ERR, __func__); + } + return sMediaLogs->RetrieveMessages(aMediaElement); +} + +/* static */ +void DecoderDoctorLogger::Log(const char* aSubjectTypeName, + const void* aSubjectPointer, + DDLogCategory aCategory, const char* aLabel, + DDLogValue&& aValue) { + if (IsDDLoggingEnabled()) { + MOZ_ASSERT(sMediaLogs); + sMediaLogs->Log(aSubjectTypeName, aSubjectPointer, aCategory, aLabel, + std::move(aValue)); + } +} + +} // namespace mozilla |