summaryrefslogtreecommitdiffstats
path: root/dom/media/doctor/DecoderDoctorLogger.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dom/media/doctor/DecoderDoctorLogger.cpp')
-rw-r--r--dom/media/doctor/DecoderDoctorLogger.cpp176
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