DecoderDoctorLogger.h (20854B)
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 #ifndef DecoderDoctorLogger_h_ 8 #define DecoderDoctorLogger_h_ 9 10 #include "DDLogCategory.h" 11 #include "DDLogValue.h" 12 #include "DDLoggedTypeTraits.h" 13 #include "mozilla/Atomics.h" 14 #include "mozilla/MozPromise.h" 15 #include "mozilla/NonDereferenceable.h" 16 #include "nsString.h" 17 18 namespace mozilla { 19 20 // Main class used to capture log messages from the media stack, and to 21 // retrieve processed messages associated with an HTMLMediaElement. 22 // 23 // The logging APIs are designed to work as fast as possible (in most cases 24 // only checking a couple of atomic variables, and not allocating memory), so 25 // as not to introduce perceptible latency. 26 // Consider using DDLOG...() macros, and IsDDLoggingEnabled(), to avoid any 27 // unneeded work when logging is not enabled. 28 // 29 // Structural logging messages are used to determine when objects are created 30 // and destroyed, and to link objects that depend on each other, ultimately 31 // tying groups of objects and their messages to HTMLMediaElement objects. 32 // 33 // A separate thread processes log messages, and can asynchronously retrieve 34 // processed messages that correspond to a given HTMLMediaElement. 35 // That thread is also responsible for removing dated messages, so as not to 36 // take too much memory. 37 class DecoderDoctorLogger { 38 public: 39 // Called by nsLayoutStatics::Initialize() before any other media work. 40 // Pre-enables logging if MOZ_LOG requires DDLogger. 41 static void Init(); 42 43 // Is logging currently enabled? This is tested anyway in all public `Log...` 44 // functions, but it may be used to prevent logging-only work in clients. 45 static inline bool IsDDLoggingEnabled() { 46 return MOZ_UNLIKELY(static_cast<LogState>(sLogState) == scEnabled); 47 } 48 49 // Shutdown logging. This will prevent more messages to be queued, but the 50 // already-queued messages may still get processed. 51 static void ShutdownLogging() { sLogState = scShutdown; } 52 53 // Something went horribly wrong, stop all logging and log processing. 54 static void Panic(const char* aReason) { 55 PanicInternal(aReason, /* aDontBlock */ false); 56 } 57 58 // Logging functions. 59 // 60 // All logging functions take: 61 // - The object that produces the message, either as a template type (for 62 // which a specialized DDLoggedTypeTraits exists), or a pointer and a type 63 // name (needed for inner classes that cannot specialize 64 // DDLoggedTypeTraits.) 65 // - A DDLogCategory defining the type of log message; some are used 66 // internally for capture the lifetime and linking of C++ objects, others 67 // are used to split messages into different domains. 68 // - A label (string literal). 69 // - An optional Variant value, see DDLogValue for the accepted types. 70 // 71 // The following `EagerLog...` functions always cause their arguments to be 72 // pre-evaluated even if logging is disabled, in which case runtime could be 73 // wasted. Consider using `DDLOG...` macros instead, or test 74 // `IsDDLoggingEnabled()` first. 75 76 template <typename Value> 77 static void EagerLogValue(const char* aSubjectTypeName, 78 const void* aSubjectPointer, 79 DDLogCategory aCategory, const char* aLabel, 80 Value&& aValue) { 81 Log(aSubjectTypeName, aSubjectPointer, aCategory, aLabel, 82 DDLogValue{std::forward<Value>(aValue)}); 83 } 84 85 template <typename Subject, typename Value> 86 static void EagerLogValue(const Subject* aSubject, DDLogCategory aCategory, 87 const char* aLabel, Value&& aValue) { 88 EagerLogValue(DDLoggedTypeTraits<Subject>::Name(), aSubject, aCategory, 89 aLabel, std::forward<Value>(aValue)); 90 } 91 92 // EagerLogValue that can explicitly take strings, as the templated function 93 // above confuses Variant when forwarding string literals. 94 static void EagerLogValue(const char* aSubjectTypeName, 95 const void* aSubjectPointer, 96 DDLogCategory aCategory, const char* aLabel, 97 const char* aValue) { 98 Log(aSubjectTypeName, aSubjectPointer, aCategory, aLabel, 99 DDLogValue{aValue}); 100 } 101 102 template <typename Subject> 103 static void EagerLogValue(const Subject* aSubject, DDLogCategory aCategory, 104 const char* aLabel, const char* aValue) { 105 EagerLogValue(DDLoggedTypeTraits<Subject>::Name(), aSubject, aCategory, 106 aLabel, aValue); 107 } 108 109 static void EagerLogPrintf(const char* aSubjectTypeName, 110 const void* aSubjectPointer, 111 DDLogCategory aCategory, const char* aLabel, 112 const char* aString) { 113 Log(aSubjectTypeName, aSubjectPointer, aCategory, aLabel, 114 DDLogValue{nsCString{aString}}); 115 } 116 117 template <typename... Args> 118 static void EagerLogPrintf(const char* aSubjectTypeName, 119 const void* aSubjectPointer, 120 DDLogCategory aCategory, const char* aLabel, 121 const char* aFormat, Args&&... aArgs) { 122 Log(aSubjectTypeName, aSubjectPointer, aCategory, aLabel, 123 DDLogValue{ 124 nsCString{nsPrintfCString(aFormat, std::forward<Args>(aArgs)...)}}); 125 } 126 127 template <typename Subject> 128 static void EagerLogPrintf(const Subject* aSubject, DDLogCategory aCategory, 129 const char* aLabel, const char* aString) { 130 EagerLogPrintf(DDLoggedTypeTraits<Subject>::Name(), aSubject, aCategory, 131 aLabel, aString); 132 } 133 134 template <typename Subject, typename... Args> 135 static void EagerLogPrintf(const Subject* aSubject, DDLogCategory aCategory, 136 const char* aLabel, const char* aFormat, 137 Args&&... aArgs) { 138 EagerLogPrintf(DDLoggedTypeTraits<Subject>::Name(), aSubject, aCategory, 139 aLabel, aFormat, std::forward<Args>(aArgs)...); 140 } 141 142 static void MozLogPrintf(const char* aSubjectTypeName, 143 const void* aSubjectPointer, 144 const LogModule* aLogModule, LogLevel aLogLevel, 145 const char* aString) { 146 Log(aSubjectTypeName, aSubjectPointer, CategoryForMozLogLevel(aLogLevel), 147 aLogModule->Name(), // LogModule name as label. 148 DDLogValue{nsCString{aString}}); 149 MOZ_LOG(aLogModule, aLogLevel, 150 ("%s[%p] %s", aSubjectTypeName, aSubjectPointer, aString)); 151 } 152 153 template <typename... Args> 154 static void MozLogPrintf(const char* aSubjectTypeName, 155 const void* aSubjectPointer, 156 const LogModule* aLogModule, LogLevel aLogLevel, 157 const char* aFormat, Args&&... aArgs) { 158 nsCString printed = nsPrintfCString(aFormat, std::forward<Args>(aArgs)...); 159 Log(aSubjectTypeName, aSubjectPointer, CategoryForMozLogLevel(aLogLevel), 160 aLogModule->Name(), // LogModule name as label. 161 DDLogValue{printed}); 162 MOZ_LOG(aLogModule, aLogLevel, 163 ("%s[%p] %s", aSubjectTypeName, aSubjectPointer, printed.get())); 164 } 165 166 template <typename Subject> 167 static void MozLogPrintf(const Subject* aSubject, const LogModule* aLogModule, 168 LogLevel aLogLevel, const char* aString) { 169 MozLogPrintf(DDLoggedTypeTraits<Subject>::Name(), aSubject, aLogModule, 170 aLogLevel, aString); 171 } 172 173 template <typename Subject, typename... Args> 174 static void MozLogPrintf(const Subject* aSubject, const LogModule* aLogModule, 175 LogLevel aLogLevel, const char* aFormat, 176 Args&&... aArgs) { 177 MozLogPrintf(DDLoggedTypeTraits<Subject>::Name(), aSubject, aLogModule, 178 aLogLevel, aFormat, std::forward<Args>(aArgs)...); 179 } 180 181 // Special logging functions. Consider using DecoderDoctorLifeLogger to 182 // automatically capture constructions & destructions. 183 184 static void LogConstruction(const char* aSubjectTypeName, 185 const void* aSubjectPointer) { 186 Log(aSubjectTypeName, aSubjectPointer, DDLogCategory::_Construction, "", 187 DDLogValue{DDNoValue{}}); 188 } 189 190 static void LogConstructionAndBase(const char* aSubjectTypeName, 191 const void* aSubjectPointer, 192 const char* aBaseTypeName, 193 const void* aBasePointer) { 194 Log(aSubjectTypeName, aSubjectPointer, DDLogCategory::_DerivedConstruction, 195 "", DDLogValue{DDLogObject{aBaseTypeName, aBasePointer}}); 196 } 197 198 template <typename B> 199 static void LogConstructionAndBase(const char* aSubjectTypeName, 200 const void* aSubjectPointer, 201 const B* aBase) { 202 Log(aSubjectTypeName, aSubjectPointer, DDLogCategory::_DerivedConstruction, 203 "", DDLogValue{DDLogObject{DDLoggedTypeTraits<B>::Name(), aBase}}); 204 } 205 206 template <typename Subject> 207 static void LogConstruction(NonDereferenceable<const Subject> aSubject) { 208 using Traits = DDLoggedTypeTraits<Subject>; 209 if (!Traits::HasBase::value) { 210 Log(DDLoggedTypeTraits<Subject>::Name(), 211 reinterpret_cast<const void*>(aSubject.value()), 212 DDLogCategory::_Construction, "", DDLogValue{DDNoValue{}}); 213 } else { 214 Log(DDLoggedTypeTraits<Subject>::Name(), 215 reinterpret_cast<const void*>(aSubject.value()), 216 DDLogCategory::_DerivedConstruction, "", 217 DDLogValue{DDLogObject{ 218 DDLoggedTypeTraits<typename Traits::BaseType>::Name(), 219 reinterpret_cast<const void*>( 220 NonDereferenceable<const typename Traits::BaseType>(aSubject) 221 .value())}}); 222 } 223 } 224 225 template <typename Subject> 226 static void LogConstruction(const Subject* aSubject) { 227 LogConstruction(NonDereferenceable<const Subject>(aSubject)); 228 } 229 230 static void LogDestruction(const char* aSubjectTypeName, 231 const void* aSubjectPointer) { 232 Log(aSubjectTypeName, aSubjectPointer, DDLogCategory::_Destruction, "", 233 DDLogValue{DDNoValue{}}); 234 } 235 236 template <typename Subject> 237 static void LogDestruction(NonDereferenceable<const Subject> aSubject) { 238 Log(DDLoggedTypeTraits<Subject>::Name(), 239 reinterpret_cast<const void*>(aSubject.value()), 240 DDLogCategory::_Destruction, "", DDLogValue{DDNoValue{}}); 241 } 242 243 template <typename Subject> 244 static void LogDestruction(const Subject* aSubject) { 245 LogDestruction(NonDereferenceable<const Subject>(aSubject)); 246 } 247 248 template <typename P, typename C> 249 static void LinkParentAndChild(const P* aParent, const char* aLinkName, 250 const C* aChild) { 251 if (aChild) { 252 Log(DDLoggedTypeTraits<P>::Name(), aParent, DDLogCategory::_Link, 253 aLinkName, 254 DDLogValue{DDLogObject{DDLoggedTypeTraits<C>::Name(), aChild}}); 255 } 256 } 257 258 template <typename C> 259 static void LinkParentAndChild(const char* aParentTypeName, 260 const void* aParentPointer, 261 const char* aLinkName, const C* aChild) { 262 if (aChild) { 263 Log(aParentTypeName, aParentPointer, DDLogCategory::_Link, aLinkName, 264 DDLogValue{DDLogObject{DDLoggedTypeTraits<C>::Name(), aChild}}); 265 } 266 } 267 268 template <typename P> 269 static void LinkParentAndChild(const P* aParent, const char* aLinkName, 270 const char* aChildTypeName, 271 const void* aChildPointer) { 272 if (aChildPointer) { 273 Log(DDLoggedTypeTraits<P>::Name(), aParent, DDLogCategory::_Link, 274 aLinkName, DDLogValue{DDLogObject{aChildTypeName, aChildPointer}}); 275 } 276 } 277 278 template <typename C> 279 static void UnlinkParentAndChild(const char* aParentTypeName, 280 const void* aParentPointer, 281 const C* aChild) { 282 if (aChild) { 283 Log(aParentTypeName, aParentPointer, DDLogCategory::_Unlink, "", 284 DDLogValue{DDLogObject{DDLoggedTypeTraits<C>::Name(), aChild}}); 285 } 286 } 287 288 template <typename P, typename C> 289 static void UnlinkParentAndChild(const P* aParent, const C* aChild) { 290 if (aChild) { 291 Log(DDLoggedTypeTraits<P>::Name(), aParent, DDLogCategory::_Unlink, "", 292 DDLogValue{DDLogObject{DDLoggedTypeTraits<C>::Name(), aChild}}); 293 } 294 } 295 296 // Retrieval functions. 297 298 // Enable logging, if not done already. No effect otherwise. 299 static void EnableLogging(); 300 301 using LogMessagesPromise = 302 MozPromise<nsCString, nsresult, /* IsExclusive = */ true>; 303 304 // Retrieve all messages related to a given HTMLMediaElement object. 305 // This call will trigger a processing run (to ensure the most recent data 306 // will be returned), and the returned promise will be resolved with all 307 // relevant log messages and object lifetimes in a JSON string. 308 // The first call will enable logging, until shutdown. 309 static RefPtr<LogMessagesPromise> RetrieveMessages( 310 const dom::HTMLMediaElement* aMediaElement); 311 312 private: 313 // If logging is not enabled yet, initiate it, return true. 314 // If logging has been shutdown, don't start it, return false. 315 // Otherwise return true. 316 static bool EnsureLogIsEnabled(); 317 318 // Note that this call may block while the state is scEnabling; 319 // set aDontBlock to true to avoid blocking, most importantly when the 320 // caller is the one doing the enabling, this would cause an endless loop. 321 static void PanicInternal(const char* aReason, bool aDontBlock); 322 323 static void Log(const char* aSubjectTypeName, const void* aSubjectPointer, 324 DDLogCategory aCategory, const char* aLabel, 325 DDLogValue&& aValue); 326 327 static void Log(const char* aSubjectTypeName, const void* aSubjectPointer, 328 const LogModule* aLogModule, LogLevel aLogLevel, 329 DDLogValue&& aValue); 330 331 static DDLogCategory CategoryForMozLogLevel(LogLevel aLevel) { 332 switch (aLevel) { 333 default: 334 case LogLevel::Error: 335 return DDLogCategory::MozLogError; 336 case LogLevel::Warning: 337 return DDLogCategory::MozLogWarning; 338 case LogLevel::Info: 339 return DDLogCategory::MozLogInfo; 340 case LogLevel::Debug: 341 return DDLogCategory::MozLogDebug; 342 case LogLevel::Verbose: 343 return DDLogCategory::MozLogVerbose; 344 } 345 } 346 347 using LogState = int; 348 // Currently disabled, may be enabled on request. 349 static constexpr LogState scDisabled = 0; 350 // Currently enabled (logging happens), may be shutdown. 351 static constexpr LogState scEnabled = 1; 352 // Still disabled, but one thread is working on enabling it, nobody else 353 // should interfere during this time. 354 static constexpr LogState scEnabling = 2; 355 // Shutdown, cannot be re-enabled. 356 static constexpr LogState scShutdown = 3; 357 // Current state. 358 // "ReleaseAcquire" because when changing to scEnabled, the just-created 359 // sMediaLogs must be accessible to consumers that see scEnabled. 360 static Atomic<LogState, ReleaseAcquire> sLogState; 361 362 // If non-null, reason for an abnormal shutdown. 363 static const char* sShutdownReason; 364 }; 365 366 // Base class to automatically record a class lifetime. Usage: 367 // class SomeClass : public DecoderDoctorLifeLogger<SomeClass> 368 // { 369 // ... 370 template <typename T> 371 class DecoderDoctorLifeLogger { 372 protected: 373 DecoderDoctorLifeLogger() { 374 DecoderDoctorLogger::LogConstruction(NonDereferenceable<const T>(this)); 375 } 376 ~DecoderDoctorLifeLogger() { 377 DecoderDoctorLogger::LogDestruction(NonDereferenceable<const T>(this)); 378 } 379 }; 380 381 // Macros to help lazily-evaluate arguments, only after we have checked that 382 // logging is enabled. 383 384 // Log a single value; see DDLogValue for allowed types. 385 #define DDLOG(_category, _label, _arg) \ 386 do { \ 387 if (DecoderDoctorLogger::IsDDLoggingEnabled()) { \ 388 DecoderDoctorLogger::EagerLogValue(this, _category, _label, _arg); \ 389 } \ 390 } while (0) 391 // Log a single value, with an EXplicit `this`. 392 #define DDLOGEX(_this, _category, _label, _arg) \ 393 do { \ 394 if (DecoderDoctorLogger::IsDDLoggingEnabled()) { \ 395 DecoderDoctorLogger::EagerLogValue(_this, _category, _label, _arg); \ 396 } \ 397 } while (0) 398 // Log a single value, with EXplicit type name and `this`. 399 #define DDLOGEX2(_typename, _this, _category, _label, _arg) \ 400 do { \ 401 if (DecoderDoctorLogger::IsDDLoggingEnabled()) { \ 402 DecoderDoctorLogger::EagerLogValue(_typename, _this, _category, _label, \ 403 _arg); \ 404 } \ 405 } while (0) 406 407 #ifdef DEBUG 408 // Do a printf format check in DEBUG, with the downside that side-effects (from 409 // evaluating the arguments) may happen twice! Who would do that anyway? 410 static void inline MOZ_FORMAT_PRINTF(1, 2) DDLOGPRCheck(const char*, ...) {} 411 # define DDLOGPR_CHECK(_fmt, ...) DDLOGPRCheck(_fmt, ##__VA_ARGS__) 412 #else 413 # define DDLOGPR_CHECK(_fmt, ...) 414 #endif 415 416 // Log a printf'd string. Discouraged, please try using DDLOG instead. 417 #define DDLOGPR(_category, _label, _format, ...) \ 418 do { \ 419 if (DecoderDoctorLogger::IsDDLoggingEnabled()) { \ 420 DDLOGPR_CHECK(_format, ##__VA_ARGS__); \ 421 DecoderDoctorLogger::EagerLogPrintf(this, _category, _label, _format, \ 422 ##__VA_ARGS__); \ 423 } \ 424 } while (0) 425 426 // Link a child object. 427 #define DDLINKCHILD(...) \ 428 do { \ 429 if (DecoderDoctorLogger::IsDDLoggingEnabled()) { \ 430 DecoderDoctorLogger::LinkParentAndChild(this, __VA_ARGS__); \ 431 } \ 432 } while (0) 433 434 // Unlink a child object. 435 #define DDUNLINKCHILD(...) \ 436 do { \ 437 if (DecoderDoctorLogger::IsDDLoggingEnabled()) { \ 438 DecoderDoctorLogger::UnlinkParentAndChild(this, __VA_ARGS__); \ 439 } \ 440 } while (0) 441 442 // Log a printf'd string to DDLogger and/or MOZ_LOG, with an EXplicit `this`. 443 // Don't even call MOZ_LOG on Android non-release/beta; See Logging.h. 444 #if !defined(ANDROID) || !defined(RELEASE_OR_BETA) 445 # define DDMOZ_LOGEX(_this, _logModule, _logLevel, _format, ...) \ 446 do { \ 447 if (DecoderDoctorLogger::IsDDLoggingEnabled() || \ 448 MOZ_LOG_TEST(_logModule, _logLevel)) { \ 449 DDLOGPR_CHECK(_format, ##__VA_ARGS__); \ 450 DecoderDoctorLogger::MozLogPrintf(_this, _logModule, _logLevel, \ 451 _format, ##__VA_ARGS__); \ 452 } \ 453 } while (0) 454 #else 455 # define DDMOZ_LOGEX(_this, _logModule, _logLevel, _format, ...) \ 456 do { \ 457 if (DecoderDoctorLogger::IsDDLoggingEnabled()) { \ 458 DDLOGPR_CHECK(_format, ##__VA_ARGS__); \ 459 DecoderDoctorLogger::MozLogPrintf(_this, _logModule, _logLevel, \ 460 _format, ##__VA_ARGS__); \ 461 } \ 462 } while (0) 463 #endif 464 465 // Log a printf'd string to DDLogger and/or MOZ_LOG. 466 #define DDMOZ_LOG(_logModule, _logLevel, _format, ...) \ 467 DDMOZ_LOGEX(this, _logModule, _logLevel, _format, ##__VA_ARGS__) 468 469 } // namespace mozilla 470 471 #endif // DecoderDoctorLogger_h_