DecoderDoctorLogger.cpp (6507B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #include "DecoderDoctorLogger.h" 8 9 #include "DDLogUtils.h" 10 #include "DDMediaLogs.h" 11 #include "mozilla/ClearOnShutdown.h" 12 #include "mozilla/SchedulerGroup.h" 13 #include "mozilla/StaticPtr.h" 14 15 namespace mozilla { 16 17 /* static */ Atomic<DecoderDoctorLogger::LogState, ReleaseAcquire> 18 DecoderDoctorLogger::sLogState{DecoderDoctorLogger::scDisabled}; 19 20 /* static */ const char* DecoderDoctorLogger::sShutdownReason = nullptr; 21 22 static DDMediaLogs* sMediaLogs; 23 24 /* static */ 25 void DecoderDoctorLogger::Init() { 26 MOZ_ASSERT(static_cast<LogState>(sLogState) == scDisabled); 27 if (MOZ_LOG_TEST(sDecoderDoctorLoggerLog, LogLevel::Error) || 28 MOZ_LOG_TEST(sDecoderDoctorLoggerEndLog, LogLevel::Error)) { 29 EnableLogging(); 30 } 31 } 32 33 #ifndef RELEASE_OR_BETA 34 // First DDLogShutdowner sets sLogState to scShutdown, to prevent further 35 // logging. 36 struct DDLogShutdowner { 37 ~DDLogShutdowner() { 38 DDL_INFO("Shutting down"); 39 // Prevent further logging, some may racily seep in, it's fine as the 40 // logging infrastructure would still be alive until DDLogDeleter runs. 41 DecoderDoctorLogger::ShutdownLogging(); 42 } 43 }; 44 static StaticAutoPtr<DDLogShutdowner> sDDLogShutdowner; 45 46 // Later DDLogDeleter will delete the message queue and media logs. 47 struct DDLogDeleter { 48 ~DDLogDeleter() { 49 if (sMediaLogs) { 50 DDL_INFO("Final processing of collected logs"); 51 delete sMediaLogs; 52 sMediaLogs = nullptr; 53 } 54 } 55 }; 56 static StaticAutoPtr<DDLogDeleter> sDDLogDeleter; 57 #endif 58 59 /* static */ 60 void DecoderDoctorLogger::PanicInternal(const char* aReason, bool aDontBlock) { 61 for (;;) { 62 const LogState state = static_cast<LogState>(sLogState); 63 if (state == scEnabling && !aDontBlock) { 64 // Wait for the end of the enabling process (unless we're in it, in which 65 // case we don't want to block.) 66 continue; 67 } 68 if (state == scShutdown) { 69 // Already shutdown, nothing more to do. 70 break; 71 } 72 if (sLogState.compareExchange(state, scShutdown)) { 73 // We are the one performing the first shutdown -> Record reason. 74 sShutdownReason = aReason; 75 // Free as much memory as possible. 76 if (sMediaLogs) { 77 // Shutdown the medialogs processing thread, and free as much memory 78 // as possible. 79 sMediaLogs->Panic(); 80 } 81 // sMediaLogs and sQueue will be deleted by DDLogDeleter. 82 // We don't want to delete them right now, because there could be a race 83 // where another thread started logging or retrieving logs before we 84 // changed the state to scShutdown, but has been delayed before actually 85 // trying to write or read log messages, thereby causing a UAF. 86 } 87 // If someone else changed the state, we'll just loop around, and either 88 // shutdown already happened elsewhere, or we'll try to shutdown again. 89 } 90 } 91 92 /* static */ 93 bool DecoderDoctorLogger::EnsureLogIsEnabled() { 94 #ifdef RELEASE_OR_BETA 95 // Just refuse to enable DDLogger on release&beta because it makes it too easy 96 // to trigger an OOM. See bug 1571648. 97 return false; 98 #else 99 for (;;) { 100 LogState state = static_cast<LogState>(sLogState); 101 switch (state) { 102 case scDisabled: 103 // Currently disabled, try to be the one to enable. 104 if (sLogState.compareExchange(scDisabled, scEnabling)) { 105 // We are the one to enable logging, state won't change (except for 106 // possible shutdown.) 107 // Create DDMediaLogs singleton, which will process the message queue. 108 DDMediaLogs::ConstructionResult mediaLogsConstruction = 109 DDMediaLogs::New(); 110 if (NS_FAILED(mediaLogsConstruction.mRv)) { 111 PanicInternal("Failed to enable logging", /* aDontBlock */ true); 112 return false; 113 } 114 MOZ_ASSERT(mediaLogsConstruction.mMediaLogs); 115 sMediaLogs = mediaLogsConstruction.mMediaLogs; 116 // Setup shutdown-time clean-up. 117 MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch( 118 NS_NewRunnableFunction("DDLogger shutdown setup", [] { 119 sDDLogShutdowner = new DDLogShutdowner(); 120 ClearOnShutdown(&sDDLogShutdowner, 121 ShutdownPhase::XPCOMShutdown); 122 sDDLogDeleter = new DDLogDeleter(); 123 ClearOnShutdown(&sDDLogDeleter, 124 ShutdownPhase::XPCOMShutdownThreads); 125 }))); 126 127 // Nobody else should change the state when *we* are enabling logging. 128 MOZ_ASSERT(sLogState == scEnabling); 129 sLogState = scEnabled; 130 DDL_INFO("Logging enabled"); 131 return true; 132 } 133 // Someone else changed the state before our compareExchange, just loop 134 // around to examine the new situation. 135 break; 136 case scEnabled: 137 return true; 138 case scEnabling: 139 // Someone else is currently enabling logging, actively wait by just 140 // looping, until the state changes. 141 break; 142 case scShutdown: 143 // Shutdown is non-recoverable, we cannot enable logging again. 144 return false; 145 } 146 // Not returned yet, loop around to examine the new situation. 147 } 148 #endif 149 } 150 151 /* static */ 152 void DecoderDoctorLogger::EnableLogging() { (void)EnsureLogIsEnabled(); } 153 154 /* static */ RefPtr<DecoderDoctorLogger::LogMessagesPromise> 155 DecoderDoctorLogger::RetrieveMessages( 156 const dom::HTMLMediaElement* aMediaElement) { 157 if (MOZ_UNLIKELY(!EnsureLogIsEnabled())) { 158 DDL_WARN("Request (for %p) but there are no logs", aMediaElement); 159 return DecoderDoctorLogger::LogMessagesPromise::CreateAndReject( 160 NS_ERROR_DOM_MEDIA_ABORT_ERR, __func__); 161 } 162 return sMediaLogs->RetrieveMessages(aMediaElement); 163 } 164 165 /* static */ 166 void DecoderDoctorLogger::Log(const char* aSubjectTypeName, 167 const void* aSubjectPointer, 168 DDLogCategory aCategory, const char* aLabel, 169 DDLogValue&& aValue) { 170 if (IsDDLoggingEnabled()) { 171 MOZ_ASSERT(sMediaLogs); 172 sMediaLogs->Log(aSubjectTypeName, aSubjectPointer, aCategory, aLabel, 173 std::move(aValue)); 174 } 175 } 176 177 } // namespace mozilla