dom/ipc/MemoryReportRequest.cpp
author Nathan Froyd <froydnj@mozilla.com>
Wed, 02 Jan 2019 16:18:13 -0500
changeset 509416 7b71897ff6be49225613180bac536673a03bb7c0
parent 505383 6f3709b3878117466168c40affa7bca0b60cf75b
child 511171 d54846d01280026138135bde6e0bb1bcfe58feae
permissions -rw-r--r--
Bug 1517276 - thread callbacks through MemoryReportRequestClient; r=mccr8 The guts of MemoryReportRequestClient's supporting runnables contain switches on the particular type of process we're running. If you're bringing up a new process type, having to add extra cases for your process type here is a bit onerous. These runnables really shouldn't know anything about the process types that they're running on, either. The easiest thing to do is modify MemoryReportRequestClient::Start to take callbacks for what to do when a report is created and when reporting is finished. Then all process-specific knowledge can be pushed out to the clients themselves, leaving MemoryReportRequestClient and friends process-type agnostic. We could even, at some later date, move this code into xpcom/base/ to sit near nsMemoryReporterManager, where it belongs.

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "MemoryReportRequest.h"

namespace mozilla {
namespace dom {

MemoryReportRequestHost::MemoryReportRequestHost(uint32_t aGeneration)
    : mGeneration(aGeneration), mSuccess(false) {
  MOZ_COUNT_CTOR(MemoryReportRequestHost);
  mReporterManager = nsMemoryReporterManager::GetOrCreate();
  NS_WARNING_ASSERTION(mReporterManager, "GetOrCreate failed");
}

void MemoryReportRequestHost::RecvReport(const MemoryReport& aReport) {
  // Skip reports from older generations. We need to do this here since we
  // could receive older reports from a subprocesses before it acknowledges
  // a new request, and we only track one active request per process.
  if (aReport.generation() != mGeneration) {
    return;
  }

  if (mReporterManager) {
    mReporterManager->HandleChildReport(mGeneration, aReport);
  }
}

void MemoryReportRequestHost::Finish(uint32_t aGeneration) {
  // Skip reports from older generations. See the comment in RecvReport.
  if (mGeneration != aGeneration) {
    return;
  }
  mSuccess = true;
}

MemoryReportRequestHost::~MemoryReportRequestHost() {
  MOZ_COUNT_DTOR(MemoryReportRequestHost);

  if (mReporterManager) {
    mReporterManager->EndProcessReport(mGeneration, mSuccess);
    mReporterManager = nullptr;
  }
}

NS_IMPL_ISUPPORTS(MemoryReportRequestClient, nsIRunnable)

/* static */ void MemoryReportRequestClient::Start(
    uint32_t aGeneration, bool aAnonymize, bool aMinimizeMemoryUsage,
    const MaybeFileDesc& aDMDFile, const nsACString& aProcessString,
    const ReportCallback& aReportCallback,
    const FinishCallback& aFinishCallback) {
  RefPtr<MemoryReportRequestClient> request = new MemoryReportRequestClient(
      aGeneration, aAnonymize, aDMDFile, aProcessString,
      aReportCallback, aFinishCallback);

  DebugOnly<nsresult> rv;
  if (aMinimizeMemoryUsage) {
    nsCOMPtr<nsIMemoryReporterManager> mgr =
        do_GetService("@mozilla.org/memory-reporter-manager;1");
    rv = mgr->MinimizeMemoryUsage(request);
    // mgr will eventually call actor->Run()
  } else {
    rv = request->Run();
  }

  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "actor operation failed");
}

MemoryReportRequestClient::MemoryReportRequestClient(
    uint32_t aGeneration, bool aAnonymize, const MaybeFileDesc& aDMDFile,
    const nsACString& aProcessString, const ReportCallback& aReportCallback,
    const FinishCallback& aFinishCallback)
    : mGeneration(aGeneration),
      mAnonymize(aAnonymize),
      mProcessString(aProcessString),
      mReportCallback(aReportCallback),
      mFinishCallback(aFinishCallback) {
  if (aDMDFile.type() == MaybeFileDesc::TFileDescriptor) {
    mDMDFile = aDMDFile.get_FileDescriptor();
  }
}

MemoryReportRequestClient::~MemoryReportRequestClient() {}

class HandleReportCallback final : public nsIHandleReportCallback {
 public:
  using ReportCallback = typename MemoryReportRequestClient::ReportCallback;

  NS_DECL_ISUPPORTS

  explicit HandleReportCallback(uint32_t aGeneration,
                                const nsACString& aProcess,
                                const ReportCallback& aReportCallback)
      : mGeneration(aGeneration),
        mProcess(aProcess),
        mReportCallback(aReportCallback) {}

  NS_IMETHOD Callback(const nsACString& aProcess, const nsACString& aPath,
                      int32_t aKind, int32_t aUnits, int64_t aAmount,
                      const nsACString& aDescription,
                      nsISupports* aUnused) override {
    MemoryReport memreport(mProcess, nsCString(aPath), aKind, aUnits, aAmount,
                           mGeneration, nsCString(aDescription));
    mReportCallback(memreport);
    return NS_OK;
  }

 private:
  ~HandleReportCallback() = default;

  uint32_t mGeneration;
  const nsCString mProcess;
  ReportCallback mReportCallback;
};

NS_IMPL_ISUPPORTS(HandleReportCallback, nsIHandleReportCallback)

class FinishReportingCallback final : public nsIFinishReportingCallback {
 public:
  using FinishCallback = typename MemoryReportRequestClient::FinishCallback;

  NS_DECL_ISUPPORTS

  explicit FinishReportingCallback(uint32_t aGeneration,
                                   const FinishCallback& aFinishCallback)
      : mGeneration(aGeneration),
        mFinishCallback(aFinishCallback) {}

  NS_IMETHOD Callback(nsISupports* aUnused) override {
    return mFinishCallback(mGeneration) ? NS_OK : NS_ERROR_FAILURE;
  }

 private:
  ~FinishReportingCallback() = default;

  uint32_t mGeneration;
  FinishCallback mFinishCallback;
};

NS_IMPL_ISUPPORTS(FinishReportingCallback, nsIFinishReportingCallback)

NS_IMETHODIMP MemoryReportRequestClient::Run() {
  nsCOMPtr<nsIMemoryReporterManager> mgr =
      do_GetService("@mozilla.org/memory-reporter-manager;1");

  // Run the reporters.  The callback will turn each measurement into a
  // MemoryReport.
  RefPtr<HandleReportCallback> handleReport =
      new HandleReportCallback(mGeneration, mProcessString, mReportCallback);
  RefPtr<FinishReportingCallback> finishReporting =
      new FinishReportingCallback(mGeneration, mFinishCallback);

  nsresult rv = mgr->GetReportsForThisProcessExtended(
      handleReport, nullptr, mAnonymize, FileDescriptorToFILE(mDMDFile, "wb"),
      finishReporting, nullptr);
  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "GetReportsForThisProcessExtended failed");
  return rv;
}

}  // namespace dom
}  // namespace mozilla