tor-browser

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

CheckerboardReportService.cpp (7620B)


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