Coverage Report

Created: 2018-09-25 14:53

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