author Nathan Froyd <>
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 */

#include "MemoryReportRequest.h"

namespace mozilla {
namespace dom {

MemoryReportRequestHost::MemoryReportRequestHost(uint32_t aGeneration)
    : mGeneration(aGeneration), mSuccess(false) {
  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) {

  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) {
  mSuccess = true;

MemoryReportRequestHost::~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 =
    rv = mgr->MinimizeMemoryUsage(request);
    // mgr will eventually call actor->Run()
  } else {
    rv = request->Run();

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

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

MemoryReportRequestClient::~MemoryReportRequestClient() {}

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


  explicit HandleReportCallback(uint32_t aGeneration,
                                const nsACString& aProcess,
                                const ReportCallback& aReportCallback)
      : mGeneration(aGeneration),
        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));
    return NS_OK;

  ~HandleReportCallback() = default;

  uint32_t mGeneration;
  const nsCString mProcess;
  ReportCallback mReportCallback;

NS_IMPL_ISUPPORTS(HandleReportCallback, nsIHandleReportCallback)

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


  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;

  ~FinishReportingCallback() = default;

  uint32_t mGeneration;
  FinishCallback mFinishCallback;

NS_IMPL_ISUPPORTS(FinishReportingCallback, nsIFinishReportingCallback)

NS_IMETHODIMP MemoryReportRequestClient::Run() {
  nsCOMPtr<nsIMemoryReporterManager> mgr =

  // 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);
                       "GetReportsForThisProcessExtended failed");
  return rv;

}  // namespace dom
}  // namespace mozilla