Coverage Report

Created: 2018-09-25 14:53

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