Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/gfx/layers/apz/util/CheckerboardReportService.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
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 "CheckerboardReportService.h"
8
9
#include "gfxPrefs.h" // for gfxPrefs
10
#include "jsapi.h" // for JS_Now
11
#include "MainThreadUtils.h" // for NS_IsMainThread
12
#include "mozilla/Assertions.h" // for MOZ_ASSERT
13
#include "mozilla/ClearOnShutdown.h" // for ClearOnShutdown
14
#include "mozilla/Unused.h"
15
#include "mozilla/dom/CheckerboardReportServiceBinding.h" // for dom::CheckerboardReports
16
#include "mozilla/gfx/GPUParent.h"
17
#include "mozilla/gfx/GPUProcessManager.h"
18
#include "nsContentUtils.h" // for nsContentUtils
19
#include "nsXULAppAPI.h"
20
21
namespace mozilla {
22
namespace layers {
23
24
/*static*/ StaticRefPtr<CheckerboardEventStorage> CheckerboardEventStorage::sInstance;
25
26
/*static*/ already_AddRefed<CheckerboardEventStorage>
27
CheckerboardEventStorage::GetInstance()
28
0
{
29
0
  // The instance in the parent process does all the work, so if this is getting
30
0
  // called in the child process something is likely wrong.
31
0
  MOZ_ASSERT(XRE_IsParentProcess());
32
0
33
0
  MOZ_ASSERT(NS_IsMainThread());
34
0
  if (!sInstance) {
35
0
    sInstance = new CheckerboardEventStorage();
36
0
    ClearOnShutdown(&sInstance);
37
0
  }
38
0
  RefPtr<CheckerboardEventStorage> instance = sInstance.get();
39
0
  return instance.forget();
40
0
}
41
42
void
43
CheckerboardEventStorage::Report(uint32_t aSeverity, const std::string& aLog)
44
0
{
45
0
  if (!NS_IsMainThread()) {
46
0
    RefPtr<Runnable> task = NS_NewRunnableFunction(
47
0
      "layers::CheckerboardEventStorage::Report", [aSeverity, aLog]() -> void {
48
0
        CheckerboardEventStorage::Report(aSeverity, aLog);
49
0
      });
50
0
    NS_DispatchToMainThread(task.forget());
51
0
    return;
52
0
  }
53
0
54
0
  if (XRE_IsGPUProcess()) {
55
0
    if (gfx::GPUParent* gpu = gfx::GPUParent::GetSingleton()) {
56
0
      nsCString log(aLog.c_str());
57
0
      Unused << gpu->SendReportCheckerboard(aSeverity, log);
58
0
    }
59
0
    return;
60
0
  }
61
0
62
0
  RefPtr<CheckerboardEventStorage> storage = GetInstance();
63
0
  storage->ReportCheckerboard(aSeverity, aLog);
64
0
}
65
66
void
67
CheckerboardEventStorage::ReportCheckerboard(uint32_t aSeverity, const std::string& aLog)
68
0
{
69
0
  MOZ_ASSERT(NS_IsMainThread());
70
0
71
0
  if (aSeverity == 0) {
72
0
    // This code assumes all checkerboard reports have a nonzero severity.
73
0
    return;
74
0
  }
75
0
76
0
  CheckerboardReport severe(aSeverity, JS_Now(), aLog);
77
0
  CheckerboardReport recent;
78
0
79
0
  // First look in the "severe" reports to see if the new one belongs in that
80
0
  // list.
81
0
  for (int i = 0; i < SEVERITY_MAX_INDEX; i++) {
82
0
    if (mCheckerboardReports[i].mSeverity >= severe.mSeverity) {
83
0
      continue;
84
0
    }
85
0
    // The new one deserves to be in the "severe" list. Take the one getting
86
0
    // bumped off the list, and put it in |recent| for possible insertion into
87
0
    // the recents list.
88
0
    recent = mCheckerboardReports[SEVERITY_MAX_INDEX - 1];
89
0
90
0
    // Shuffle the severe list down, insert the new one.
91
0
    for (int j = SEVERITY_MAX_INDEX - 1; j > i; j--) {
92
0
      mCheckerboardReports[j] = mCheckerboardReports[j - 1];
93
0
    }
94
0
    mCheckerboardReports[i] = severe;
95
0
    severe.mSeverity = 0; // mark |severe| as inserted
96
0
    break;
97
0
  }
98
0
99
0
  // If |severe.mSeverity| is nonzero, the incoming report didn't get inserted
100
0
  // into the severe list; put it into |recent| for insertion into the recent
101
0
  // list.
102
0
  if (severe.mSeverity) {
103
0
    MOZ_ASSERT(recent.mSeverity == 0, "recent should be empty here");
104
0
    recent = severe;
105
0
  } // else |recent| may hold a report that got knocked out of the severe list.
106
0
107
0
  if (recent.mSeverity == 0) {
108
0
    // Nothing to be inserted into the recent list.
109
0
    return;
110
0
  }
111
0
112
0
  // If it wasn't in the "severe" list, add it to the "recent" list.
113
0
  for (int i = SEVERITY_MAX_INDEX; i < RECENT_MAX_INDEX; i++) {
114
0
    if (mCheckerboardReports[i].mTimestamp >= recent.mTimestamp) {
115
0
      continue;
116
0
    }
117
0
    // |recent| needs to be inserted at |i|. Shuffle the remaining ones down
118
0
    // and insert it.
119
0
    for (int j = RECENT_MAX_INDEX - 1; j > i; j--) {
120
0
      mCheckerboardReports[j] = mCheckerboardReports[j - 1];
121
0
    }
122
0
    mCheckerboardReports[i] = recent;
123
0
    break;
124
0
  }
125
0
}
126
127
void
128
CheckerboardEventStorage::GetReports(nsTArray<dom::CheckerboardReport>& aOutReports)
129
0
{
130
0
  MOZ_ASSERT(NS_IsMainThread());
131
0
132
0
  for (int i = 0; i < RECENT_MAX_INDEX; i++) {
133
0
    CheckerboardReport& r = mCheckerboardReports[i];
134
0
    if (r.mSeverity == 0) {
135
0
      continue;
136
0
    }
137
0
    dom::CheckerboardReport report;
138
0
    report.mSeverity.Construct() = r.mSeverity;
139
0
    report.mTimestamp.Construct() = r.mTimestamp / 1000; // micros to millis
140
0
    report.mLog.Construct() = NS_ConvertUTF8toUTF16(r.mLog.c_str(), r.mLog.size());
141
0
    report.mReason.Construct() = (i < SEVERITY_MAX_INDEX)
142
0
        ? dom::CheckerboardReason::Severe
143
0
        : dom::CheckerboardReason::Recent;
144
0
    aOutReports.AppendElement(report);
145
0
  }
146
0
}
147
148
} // namespace layers
149
150
namespace dom {
151
152
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(CheckerboardReportService, mParent)
153
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(CheckerboardReportService, AddRef)
154
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(CheckerboardReportService, Release)
155
156
/*static*/ bool
157
CheckerboardReportService::IsEnabled(JSContext* aCtx, JSObject* aGlobal)
158
0
{
159
0
  // Only allow this in the parent process
160
0
  if (!XRE_IsParentProcess()) {
161
0
    return false;
162
0
  }
163
0
  // Allow privileged code or about:checkerboard (unprivileged) to access this.
164
0
  return nsContentUtils::IsSystemCaller(aCtx)
165
0
      || nsContentUtils::IsSpecificAboutPage(aGlobal, "about:checkerboard");
166
0
}
167
168
/*static*/ already_AddRefed<CheckerboardReportService>
169
CheckerboardReportService::Constructor(const dom::GlobalObject& aGlobal, ErrorResult& aRv)
170
0
{
171
0
  RefPtr<CheckerboardReportService> ces = new CheckerboardReportService(aGlobal.GetAsSupports());
172
0
  return ces.forget();
173
0
}
174
175
CheckerboardReportService::CheckerboardReportService(nsISupports* aParent)
176
  : mParent(aParent)
177
0
{
178
0
}
179
180
JSObject*
181
CheckerboardReportService::WrapObject(JSContext* aCtx, JS::Handle<JSObject*> aGivenProto)
182
0
{
183
0
  return CheckerboardReportService_Binding::Wrap(aCtx, this, aGivenProto);
184
0
}
185
186
nsISupports*
187
CheckerboardReportService::GetParentObject()
188
0
{
189
0
  return mParent;
190
0
}
191
192
void
193
CheckerboardReportService::GetReports(nsTArray<dom::CheckerboardReport>& aOutReports)
194
0
{
195
0
  RefPtr<mozilla::layers::CheckerboardEventStorage> instance =
196
0
      mozilla::layers::CheckerboardEventStorage::GetInstance();
197
0
  MOZ_ASSERT(instance);
198
0
  instance->GetReports(aOutReports);
199
0
}
200
201
bool
202
CheckerboardReportService::IsRecordingEnabled() const
203
0
{
204
0
  return gfxPrefs::APZRecordCheckerboarding();
205
0
}
206
207
void
208
CheckerboardReportService::SetRecordingEnabled(bool aEnabled)
209
0
{
210
0
  gfxPrefs::SetAPZRecordCheckerboarding(aEnabled);
211
0
}
212
213
void
214
CheckerboardReportService::FlushActiveReports()
215
0
{
216
0
  MOZ_ASSERT(XRE_IsParentProcess());
217
0
  gfx::GPUProcessManager* gpu = gfx::GPUProcessManager::Get();
218
0
  if (gpu && gpu->NotifyGpuObservers("APZ:FlushActiveCheckerboard")) {
219
0
    return;
220
0
  }
221
0
222
0
  nsCOMPtr<nsIObserverService> obsSvc = mozilla::services::GetObserverService();
223
0
  MOZ_ASSERT(obsSvc);
224
0
  if (obsSvc) {
225
0
    obsSvc->NotifyObservers(nullptr, "APZ:FlushActiveCheckerboard", nullptr);
226
0
  }
227
0
}
228
229
} // namespace dom
230
} // namespace mozilla