tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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_