forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			575 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			575 lines
		
	
	
	
		
			20 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
 | 
						|
/* 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 "FOGIPC.h"
 | 
						|
 | 
						|
#include <limits>
 | 
						|
#include "mozilla/glean/fog_ffi_generated.h"
 | 
						|
#include "mozilla/glean/GleanMetrics.h"
 | 
						|
#include "mozilla/dom/BrowsingContextGroup.h"
 | 
						|
#include "mozilla/dom/ContentChild.h"
 | 
						|
#include "mozilla/dom/ContentParent.h"
 | 
						|
#include "mozilla/dom/DocGroup.h"
 | 
						|
#include "mozilla/dom/Document.h"
 | 
						|
#include "mozilla/dom/Promise.h"
 | 
						|
#include "mozilla/gfx/GPUChild.h"
 | 
						|
#include "mozilla/gfx/GPUParent.h"
 | 
						|
#include "mozilla/gfx/GPUProcessManager.h"
 | 
						|
#include "mozilla/Hal.h"
 | 
						|
#include "mozilla/MozPromise.h"
 | 
						|
#include "mozilla/net/SocketProcessChild.h"
 | 
						|
#include "mozilla/net/SocketProcessParent.h"
 | 
						|
#include "mozilla/ProcInfo.h"
 | 
						|
#include "mozilla/RDDChild.h"
 | 
						|
#include "mozilla/RDDParent.h"
 | 
						|
#include "mozilla/RDDProcessManager.h"
 | 
						|
#include "mozilla/ipc/UtilityProcessChild.h"
 | 
						|
#include "mozilla/ipc/UtilityProcessManager.h"
 | 
						|
#include "mozilla/ipc/UtilityProcessParent.h"
 | 
						|
#include "mozilla/ipc/UtilityProcessSandboxing.h"
 | 
						|
#include "mozilla/Unused.h"
 | 
						|
#include "GMPPlatform.h"
 | 
						|
#include "GMPServiceParent.h"
 | 
						|
#include "nsIClassifiedChannel.h"
 | 
						|
#include "nsIXULRuntime.h"
 | 
						|
#include "nsTArray.h"
 | 
						|
#include "nsThreadUtils.h"
 | 
						|
 | 
						|
using mozilla::dom::ContentParent;
 | 
						|
using mozilla::gfx::GPUChild;
 | 
						|
using mozilla::gfx::GPUProcessManager;
 | 
						|
using mozilla::ipc::ByteBuf;
 | 
						|
using mozilla::ipc::UtilityProcessChild;
 | 
						|
using mozilla::ipc::UtilityProcessManager;
 | 
						|
using mozilla::ipc::UtilityProcessParent;
 | 
						|
using FlushFOGDataPromise = mozilla::dom::ContentParent::FlushFOGDataPromise;
 | 
						|
 | 
						|
namespace geckoprofiler::markers {
 | 
						|
 | 
						|
using namespace mozilla;
 | 
						|
 | 
						|
struct ProcessingTimeMarker {
 | 
						|
  static constexpr Span<const char> MarkerTypeName() {
 | 
						|
    return MakeStringSpan("ProcessingTime");
 | 
						|
  }
 | 
						|
  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
 | 
						|
                                   int64_t aDiffMs,
 | 
						|
                                   const ProfilerString8View& aType,
 | 
						|
                                   const ProfilerString8View& aTrackerType) {
 | 
						|
    aWriter.IntProperty("time", aDiffMs);
 | 
						|
    aWriter.StringProperty("label", aType);
 | 
						|
    if (aTrackerType.Length() > 0) {
 | 
						|
      aWriter.StringProperty("tracker", aTrackerType);
 | 
						|
    }
 | 
						|
  }
 | 
						|
  static MarkerSchema MarkerTypeDisplay() {
 | 
						|
    using MS = MarkerSchema;
 | 
						|
    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
 | 
						|
    schema.AddKeyLabelFormat("time", "Recorded Time", MS::Format::Milliseconds);
 | 
						|
    schema.AddKeyLabelFormat("tracker", "Tracker Type", MS::Format::String);
 | 
						|
    schema.SetTooltipLabel("{marker.name} - {marker.data.label}");
 | 
						|
    schema.SetTableLabel(
 | 
						|
        "{marker.name} - {marker.data.label}: {marker.data.time}");
 | 
						|
    return schema;
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
}  // namespace geckoprofiler::markers
 | 
						|
 | 
						|
namespace mozilla::glean {
 | 
						|
 | 
						|
// Echoes processtools/metrics.yaml's power.wakeups_per_thread
 | 
						|
enum ProcessType {
 | 
						|
  eParentActive,
 | 
						|
  eParentInactive,
 | 
						|
  eContentForeground,
 | 
						|
  eContentBackground,
 | 
						|
  eGpuProcess,
 | 
						|
  eUnknown,
 | 
						|
};
 | 
						|
 | 
						|
// This static global is set within RecordPowerMetrics on the main thread,
 | 
						|
// using information only gettable on the main thread, and is read within
 | 
						|
// RecordThreadCpuUse on any thread.
 | 
						|
static Atomic<ProcessType> gThisProcessType(eUnknown);
 | 
						|
 | 
						|
#ifdef NIGHTLY_BUILD
 | 
						|
// It is fine to call RecordThreadCpuUse during startup before the first
 | 
						|
// RecordPowerMetrics call. In that case the parent process will be recorded
 | 
						|
// as inactive, and other processes will be ignored (content processes start
 | 
						|
// in the 'prealloc' type for which we don't record per-thread CPU use data).
 | 
						|
 | 
						|
void RecordThreadCpuUse(const nsACString& aThreadName, uint64_t aCpuTimeMs,
 | 
						|
                        uint64_t aWakeCount) {
 | 
						|
  ProcessType processType = gThisProcessType;
 | 
						|
 | 
						|
  if (processType == ProcessType::eUnknown) {
 | 
						|
    if (XRE_IsParentProcess()) {
 | 
						|
      // During startup we might not have gotten a RecordPowerMetrics call.
 | 
						|
      // That's fine. Default to eParentInactive.
 | 
						|
      processType = eParentInactive;
 | 
						|
    } else {
 | 
						|
      // We are not interested in per-thread CPU use data for the current
 | 
						|
      // process type.
 | 
						|
      return;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  nsAutoCString threadName(aThreadName);
 | 
						|
  for (size_t i = 0; i < threadName.Length(); ++i) {
 | 
						|
    const char c = threadName.CharAt(i);
 | 
						|
 | 
						|
    // Valid characters.
 | 
						|
    if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || c == '-' ||
 | 
						|
        c == '_') {
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // Should only use lower case characters
 | 
						|
    if (c >= 'A' && c <= 'Z') {
 | 
						|
      threadName.SetCharAt(c + ('a' - 'A'), i);
 | 
						|
      continue;
 | 
						|
    }
 | 
						|
 | 
						|
    // Replace everything else with _
 | 
						|
    threadName.SetCharAt('_', i);
 | 
						|
  }
 | 
						|
 | 
						|
  if (aCpuTimeMs != 0 &&
 | 
						|
      MOZ_LIKELY(aCpuTimeMs < std::numeric_limits<int32_t>::max())) {
 | 
						|
    switch (processType) {
 | 
						|
      case eParentActive:
 | 
						|
        power_cpu_ms_per_thread::parent_active.Get(threadName)
 | 
						|
            .Add(int32_t(aCpuTimeMs));
 | 
						|
        break;
 | 
						|
      case eParentInactive:
 | 
						|
        power_cpu_ms_per_thread::parent_inactive.Get(threadName)
 | 
						|
            .Add(int32_t(aCpuTimeMs));
 | 
						|
        break;
 | 
						|
      case eContentForeground:
 | 
						|
        power_cpu_ms_per_thread::content_foreground.Get(threadName)
 | 
						|
            .Add(int32_t(aCpuTimeMs));
 | 
						|
        break;
 | 
						|
      case eContentBackground:
 | 
						|
        power_cpu_ms_per_thread::content_background.Get(threadName)
 | 
						|
            .Add(int32_t(aCpuTimeMs));
 | 
						|
        break;
 | 
						|
      case eGpuProcess:
 | 
						|
        power_cpu_ms_per_thread::gpu_process.Get(threadName)
 | 
						|
            .Add(int32_t(aCpuTimeMs));
 | 
						|
        break;
 | 
						|
      case eUnknown:
 | 
						|
        // Nothing to do.
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (aWakeCount != 0 &&
 | 
						|
      MOZ_LIKELY(aWakeCount < std::numeric_limits<int32_t>::max())) {
 | 
						|
    switch (processType) {
 | 
						|
      case eParentActive:
 | 
						|
        power_wakeups_per_thread::parent_active.Get(threadName)
 | 
						|
            .Add(int32_t(aWakeCount));
 | 
						|
        break;
 | 
						|
      case eParentInactive:
 | 
						|
        power_wakeups_per_thread::parent_inactive.Get(threadName)
 | 
						|
            .Add(int32_t(aWakeCount));
 | 
						|
        break;
 | 
						|
      case eContentForeground:
 | 
						|
        power_wakeups_per_thread::content_foreground.Get(threadName)
 | 
						|
            .Add(int32_t(aWakeCount));
 | 
						|
        break;
 | 
						|
      case eContentBackground:
 | 
						|
        power_wakeups_per_thread::content_background.Get(threadName)
 | 
						|
            .Add(int32_t(aWakeCount));
 | 
						|
        break;
 | 
						|
      case eGpuProcess:
 | 
						|
        power_wakeups_per_thread::gpu_process.Get(threadName)
 | 
						|
            .Add(int32_t(aWakeCount));
 | 
						|
        break;
 | 
						|
      case eUnknown:
 | 
						|
        // Nothing to do.
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
void GetTrackerType(nsAutoCString& aTrackerType) {
 | 
						|
  using namespace mozilla::dom;
 | 
						|
  uint32_t trackingFlags =
 | 
						|
      (nsIClassifiedChannel::CLASSIFIED_CRYPTOMINING |
 | 
						|
       nsIClassifiedChannel::CLASSIFIED_FINGERPRINTING |
 | 
						|
       nsIClassifiedChannel::CLASSIFIED_TRACKING |
 | 
						|
       nsIClassifiedChannel::CLASSIFIED_TRACKING_AD |
 | 
						|
       nsIClassifiedChannel::CLASSIFIED_TRACKING_ANALYTICS |
 | 
						|
       nsIClassifiedChannel::CLASSIFIED_TRACKING_SOCIAL);
 | 
						|
  AutoTArray<RefPtr<BrowsingContextGroup>, 5> bcGroups;
 | 
						|
  BrowsingContextGroup::GetAllGroups(bcGroups);
 | 
						|
  for (auto& bcGroup : bcGroups) {
 | 
						|
    AutoTArray<DocGroup*, 5> docGroups;
 | 
						|
    bcGroup->GetDocGroups(docGroups);
 | 
						|
    for (auto* docGroup : docGroups) {
 | 
						|
      for (Document* doc : *docGroup) {
 | 
						|
        nsCOMPtr<nsIClassifiedChannel> classifiedChannel =
 | 
						|
            do_QueryInterface(doc->GetChannel());
 | 
						|
        if (classifiedChannel) {
 | 
						|
          uint32_t classificationFlags =
 | 
						|
              classifiedChannel->GetThirdPartyClassificationFlags();
 | 
						|
          trackingFlags &= classificationFlags;
 | 
						|
          if (!trackingFlags) {
 | 
						|
            return;
 | 
						|
          }
 | 
						|
        }
 | 
						|
      }
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  // The if-elseif-else chain works because the tracker types listed here are
 | 
						|
  // currently mutually exclusive and should be maintained that way by policy.
 | 
						|
  if (trackingFlags == nsIClassifiedChannel::CLASSIFIED_TRACKING_AD) {
 | 
						|
    aTrackerType = "ad";
 | 
						|
  } else if (trackingFlags ==
 | 
						|
             nsIClassifiedChannel::CLASSIFIED_TRACKING_ANALYTICS) {
 | 
						|
    aTrackerType = "analytics";
 | 
						|
  } else if (trackingFlags ==
 | 
						|
             nsIClassifiedChannel::CLASSIFIED_TRACKING_SOCIAL) {
 | 
						|
    aTrackerType = "social";
 | 
						|
  } else if (trackingFlags == nsIClassifiedChannel::CLASSIFIED_CRYPTOMINING) {
 | 
						|
    aTrackerType = "cryptomining";
 | 
						|
  } else if (trackingFlags == nsIClassifiedChannel::CLASSIFIED_FINGERPRINTING) {
 | 
						|
    aTrackerType = "fingerprinting";
 | 
						|
  } else if (trackingFlags == nsIClassifiedChannel::CLASSIFIED_TRACKING) {
 | 
						|
    // CLASSIFIED_TRACKING means we were not able to identify the type of
 | 
						|
    // classification.
 | 
						|
    aTrackerType = "unknown";
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
void RecordPowerMetrics() {
 | 
						|
  static uint64_t previousCpuTime = 0, previousGpuTime = 0;
 | 
						|
 | 
						|
  uint64_t cpuTime, newCpuTime = 0;
 | 
						|
  if (NS_SUCCEEDED(GetCpuTimeSinceProcessStartInMs(&cpuTime)) &&
 | 
						|
      cpuTime > previousCpuTime) {
 | 
						|
    newCpuTime = cpuTime - previousCpuTime;
 | 
						|
  }
 | 
						|
 | 
						|
  uint64_t gpuTime, newGpuTime = 0;
 | 
						|
  // Avoid loading gdi32.dll for the Socket process where the GPU is never used.
 | 
						|
  if (!XRE_IsSocketProcess() &&
 | 
						|
      NS_SUCCEEDED(GetGpuTimeSinceProcessStartInMs(&gpuTime)) &&
 | 
						|
      gpuTime > previousGpuTime) {
 | 
						|
    newGpuTime = gpuTime - previousGpuTime;
 | 
						|
  }
 | 
						|
 | 
						|
  if (!newCpuTime && !newGpuTime) {
 | 
						|
    // Nothing to record.
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // Compute the process type string.
 | 
						|
  nsAutoCString type(XRE_GetProcessTypeString());
 | 
						|
  nsAutoCString trackerType;
 | 
						|
  if (XRE_IsContentProcess()) {
 | 
						|
    auto* cc = dom::ContentChild::GetSingleton();
 | 
						|
    if (cc) {
 | 
						|
      type.Assign(dom::RemoteTypePrefix(cc->GetRemoteType()));
 | 
						|
      if (StringBeginsWith(type, WEB_REMOTE_TYPE)) {
 | 
						|
        type.AssignLiteral("web");
 | 
						|
        switch (cc->GetProcessPriority()) {
 | 
						|
          case hal::PROCESS_PRIORITY_BACKGROUND:
 | 
						|
            type.AppendLiteral(".background");
 | 
						|
            gThisProcessType = ProcessType::eContentBackground;
 | 
						|
            break;
 | 
						|
          case hal::PROCESS_PRIORITY_FOREGROUND:
 | 
						|
            type.AppendLiteral(".foreground");
 | 
						|
            gThisProcessType = ProcessType::eContentForeground;
 | 
						|
            break;
 | 
						|
          case hal::PROCESS_PRIORITY_BACKGROUND_PERCEIVABLE:
 | 
						|
            type.AppendLiteral(".background-perceivable");
 | 
						|
            gThisProcessType = ProcessType::eUnknown;
 | 
						|
            break;
 | 
						|
          default:
 | 
						|
            gThisProcessType = ProcessType::eUnknown;
 | 
						|
            break;
 | 
						|
        }
 | 
						|
      }
 | 
						|
      GetTrackerType(trackerType);
 | 
						|
    } else {
 | 
						|
      gThisProcessType = ProcessType::eUnknown;
 | 
						|
    }
 | 
						|
  } else if (XRE_IsParentProcess()) {
 | 
						|
    if (nsContentUtils::GetUserIsInteracting()) {
 | 
						|
      type.AssignLiteral("parent.active");
 | 
						|
      gThisProcessType = ProcessType::eParentActive;
 | 
						|
    } else {
 | 
						|
      type.AssignLiteral("parent.inactive");
 | 
						|
      gThisProcessType = ProcessType::eParentInactive;
 | 
						|
    }
 | 
						|
    hal::WakeLockInformation info;
 | 
						|
    GetWakeLockInfo(u"video-playing"_ns, &info);
 | 
						|
    if (info.numLocks() != 0 && info.numHidden() < info.numLocks()) {
 | 
						|
      type.AppendLiteral(".playing-video");
 | 
						|
    } else {
 | 
						|
      GetWakeLockInfo(u"audio-playing"_ns, &info);
 | 
						|
      if (info.numLocks()) {
 | 
						|
        type.AppendLiteral(".playing-audio");
 | 
						|
      }
 | 
						|
    }
 | 
						|
  } else if (XRE_IsGPUProcess()) {
 | 
						|
    gThisProcessType = ProcessType::eGpuProcess;
 | 
						|
  } else {
 | 
						|
    gThisProcessType = ProcessType::eUnknown;
 | 
						|
  }
 | 
						|
 | 
						|
  if (newCpuTime) {
 | 
						|
    // The counters are reset at least once a day. Assuming all cores are used
 | 
						|
    // continuously, an int32 can hold the data for 24.85 cores.
 | 
						|
    // This should be fine for now, but may overflow in the future.
 | 
						|
    // Bug 1751277 tracks a newer, bigger counter.
 | 
						|
    int32_t nNewCpuTime = int32_t(newCpuTime);
 | 
						|
    if (newCpuTime < std::numeric_limits<int32_t>::max()) {
 | 
						|
      power::total_cpu_time_ms.Add(nNewCpuTime);
 | 
						|
      power::cpu_time_per_process_type_ms.Get(type).Add(nNewCpuTime);
 | 
						|
      if (!trackerType.IsEmpty()) {
 | 
						|
        power::cpu_time_per_tracker_type_ms.Get(trackerType).Add(nNewCpuTime);
 | 
						|
      }
 | 
						|
    } else {
 | 
						|
      power::cpu_time_bogus_values.Add(1);
 | 
						|
    }
 | 
						|
    PROFILER_MARKER("Process CPU Time", OTHER, {}, ProcessingTimeMarker,
 | 
						|
                    nNewCpuTime, type, trackerType);
 | 
						|
    previousCpuTime += newCpuTime;
 | 
						|
  }
 | 
						|
 | 
						|
  if (newGpuTime) {
 | 
						|
    int32_t nNewGpuTime = int32_t(newGpuTime);
 | 
						|
    if (newGpuTime < std::numeric_limits<int32_t>::max()) {
 | 
						|
      power::total_gpu_time_ms.Add(nNewGpuTime);
 | 
						|
      power::gpu_time_per_process_type_ms.Get(type).Add(nNewGpuTime);
 | 
						|
    } else {
 | 
						|
      power::gpu_time_bogus_values.Add(1);
 | 
						|
    }
 | 
						|
    PROFILER_MARKER("Process GPU Time", OTHER, {}, ProcessingTimeMarker,
 | 
						|
                    nNewGpuTime, type, trackerType);
 | 
						|
    previousGpuTime += newGpuTime;
 | 
						|
  }
 | 
						|
 | 
						|
  profiler_record_wakeup_count(type);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Flush your data ASAP, either because the parent process is asking you to
 | 
						|
 * or because the process is about to shutdown.
 | 
						|
 *
 | 
						|
 * @param aResolver - The function you need to call with the bincoded,
 | 
						|
 *                    serialized payload that the Rust impl hands you.
 | 
						|
 */
 | 
						|
void FlushFOGData(std::function<void(ipc::ByteBuf&&)>&& aResolver) {
 | 
						|
  // Record power metrics right before data is sent to the parent.
 | 
						|
  RecordPowerMetrics();
 | 
						|
 | 
						|
  ByteBuf buf;
 | 
						|
  uint32_t ipcBufferSize = impl::fog_serialize_ipc_buf();
 | 
						|
  bool ok = buf.Allocate(ipcBufferSize);
 | 
						|
  if (!ok) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  uint32_t writtenLen = impl::fog_give_ipc_buf(buf.mData, buf.mLen);
 | 
						|
  if (writtenLen != ipcBufferSize) {
 | 
						|
    return;
 | 
						|
  }
 | 
						|
  aResolver(std::move(buf));
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Called by FOG on the parent process when it wants to flush all its
 | 
						|
 * children's data.
 | 
						|
 * @param aResolver - The function that'll be called with the results.
 | 
						|
 */
 | 
						|
void FlushAllChildData(
 | 
						|
    std::function<void(nsTArray<ipc::ByteBuf>&&)>&& aResolver) {
 | 
						|
  auto timerId = fog_ipc::flush_durations.Start();
 | 
						|
 | 
						|
  nsTArray<ContentParent*> parents;
 | 
						|
  ContentParent::GetAll(parents);
 | 
						|
  nsTArray<RefPtr<FlushFOGDataPromise>> promises;
 | 
						|
  for (auto* parent : parents) {
 | 
						|
    promises.EmplaceBack(parent->SendFlushFOGData());
 | 
						|
  }
 | 
						|
 | 
						|
  if (GPUProcessManager* gpuManager = GPUProcessManager::Get()) {
 | 
						|
    if (GPUChild* gpuChild = gpuManager->GetGPUChild()) {
 | 
						|
      promises.EmplaceBack(gpuChild->SendFlushFOGData());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (RDDProcessManager* rddManager = RDDProcessManager::Get()) {
 | 
						|
    if (RDDChild* rddChild = rddManager->GetRDDChild()) {
 | 
						|
      promises.EmplaceBack(rddChild->SendFlushFOGData());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (net::SocketProcessParent* socketParent =
 | 
						|
          net::SocketProcessParent::GetSingleton()) {
 | 
						|
    promises.EmplaceBack(socketParent->SendFlushFOGData());
 | 
						|
  }
 | 
						|
 | 
						|
  if (RefPtr<mozilla::gmp::GeckoMediaPluginServiceParent> gmps =
 | 
						|
          mozilla::gmp::GeckoMediaPluginServiceParent::GetSingleton()) {
 | 
						|
    // There can be multiple Gecko Media Plugin processes, but iterating
 | 
						|
    // through them requires locking a mutex and the IPCs need to be sent
 | 
						|
    // from a different thread, so it's better to let the
 | 
						|
    // GeckoMediaPluginServiceParent code do it for us.
 | 
						|
    gmps->SendFlushFOGData(promises);
 | 
						|
  }
 | 
						|
 | 
						|
  if (RefPtr<UtilityProcessManager> utilityManager =
 | 
						|
          UtilityProcessManager::GetIfExists()) {
 | 
						|
    for (RefPtr<UtilityProcessParent>& parent :
 | 
						|
         utilityManager->GetAllProcessesProcessParent()) {
 | 
						|
      promises.EmplaceBack(parent->SendFlushFOGData());
 | 
						|
    }
 | 
						|
  }
 | 
						|
 | 
						|
  if (promises.Length() == 0) {
 | 
						|
    // No child processes at the moment. Resolve synchronously.
 | 
						|
    fog_ipc::flush_durations.Cancel(std::move(timerId));
 | 
						|
    nsTArray<ipc::ByteBuf> results;
 | 
						|
    aResolver(std::move(results));
 | 
						|
    return;
 | 
						|
  }
 | 
						|
 | 
						|
  // If fog.ipc.flush_failures ever gets too high:
 | 
						|
  // TODO: Don't throw away resolved data if some of the promises reject.
 | 
						|
  // (not sure how, but it'll mean not using ::All... maybe a custom copy of
 | 
						|
  // AllPromiseHolder? Might be impossible outside MozPromise.h)
 | 
						|
  FlushFOGDataPromise::All(GetCurrentSerialEventTarget(), promises)
 | 
						|
      ->Then(GetCurrentSerialEventTarget(), __func__,
 | 
						|
             [aResolver = std::move(aResolver), timerId](
 | 
						|
                 FlushFOGDataPromise::AllPromiseType::ResolveOrRejectValue&&
 | 
						|
                     aValue) {
 | 
						|
               fog_ipc::flush_durations.StopAndAccumulate(std::move(timerId));
 | 
						|
               if (aValue.IsResolve()) {
 | 
						|
                 aResolver(std::move(aValue.ResolveValue()));
 | 
						|
               } else {
 | 
						|
                 fog_ipc::flush_failures.Add(1);
 | 
						|
                 nsTArray<ipc::ByteBuf> results;
 | 
						|
                 aResolver(std::move(results));
 | 
						|
               }
 | 
						|
             });
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * A child process has sent you this buf as a treat.
 | 
						|
 * @param buf - a bincoded serialized payload that the Rust impl understands.
 | 
						|
 */
 | 
						|
void FOGData(ipc::ByteBuf&& buf) {
 | 
						|
  JOG::EnsureRuntimeMetricsRegistered();
 | 
						|
  fog_ipc::buffer_sizes.Accumulate(buf.mLen);
 | 
						|
  impl::fog_use_ipc_buf(buf.mData, buf.mLen);
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Called by FOG on a child process when it wants to send a buf to the parent.
 | 
						|
 * @param buf - a bincoded serialized payload that the Rust impl understands.
 | 
						|
 */
 | 
						|
void SendFOGData(ipc::ByteBuf&& buf) {
 | 
						|
  switch (XRE_GetProcessType()) {
 | 
						|
    case GeckoProcessType_Content:
 | 
						|
      mozilla::dom::ContentChild::GetSingleton()->SendFOGData(std::move(buf));
 | 
						|
      break;
 | 
						|
    case GeckoProcessType_GMPlugin: {
 | 
						|
      mozilla::gmp::SendFOGData(std::move(buf));
 | 
						|
    } break;
 | 
						|
    case GeckoProcessType_GPU:
 | 
						|
      Unused << mozilla::gfx::GPUParent::GetSingleton()->SendFOGData(
 | 
						|
          std::move(buf));
 | 
						|
      break;
 | 
						|
    case GeckoProcessType_RDD:
 | 
						|
      Unused << mozilla::RDDParent::GetSingleton()->SendFOGData(std::move(buf));
 | 
						|
      break;
 | 
						|
    case GeckoProcessType_Socket:
 | 
						|
      Unused << net::SocketProcessChild::GetSingleton()->SendFOGData(
 | 
						|
          std::move(buf));
 | 
						|
      break;
 | 
						|
    case GeckoProcessType_Utility:
 | 
						|
      Unused << ipc::UtilityProcessChild::GetSingleton()->SendFOGData(
 | 
						|
          std::move(buf));
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      MOZ_ASSERT_UNREACHABLE("Unsuppored process type");
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
/**
 | 
						|
 * Called on the parent process to ask all child processes for data,
 | 
						|
 * sending it all down into Rust to be used.
 | 
						|
 */
 | 
						|
RefPtr<GenericPromise> FlushAndUseFOGData() {
 | 
						|
  // Record power metrics on the parent before sending requests to child
 | 
						|
  // processes.
 | 
						|
  RecordPowerMetrics();
 | 
						|
 | 
						|
  RefPtr<GenericPromise::Private> ret = new GenericPromise::Private(__func__);
 | 
						|
  std::function<void(nsTArray<ByteBuf>&&)> resolver =
 | 
						|
      [ret](nsTArray<ByteBuf>&& bufs) {
 | 
						|
        for (ByteBuf& buf : bufs) {
 | 
						|
          FOGData(std::move(buf));
 | 
						|
        }
 | 
						|
        ret->Resolve(true, __func__);
 | 
						|
      };
 | 
						|
  FlushAllChildData(std::move(resolver));
 | 
						|
  return ret;
 | 
						|
}
 | 
						|
 | 
						|
void TestTriggerMetrics(uint32_t aProcessType,
 | 
						|
                        const RefPtr<dom::Promise>& promise) {
 | 
						|
  switch (aProcessType) {
 | 
						|
    case nsIXULRuntime::PROCESS_TYPE_GMPLUGIN: {
 | 
						|
      RefPtr<mozilla::gmp::GeckoMediaPluginServiceParent> gmps(
 | 
						|
          mozilla::gmp::GeckoMediaPluginServiceParent::GetSingleton());
 | 
						|
      gmps->TestTriggerMetrics()->Then(
 | 
						|
          GetCurrentSerialEventTarget(), __func__,
 | 
						|
          [promise]() { promise->MaybeResolveWithUndefined(); },
 | 
						|
          [promise]() { promise->MaybeRejectWithUndefined(); });
 | 
						|
    } break;
 | 
						|
    case nsIXULRuntime::PROCESS_TYPE_GPU:
 | 
						|
      gfx::GPUProcessManager::Get()->TestTriggerMetrics()->Then(
 | 
						|
          GetCurrentSerialEventTarget(), __func__,
 | 
						|
          [promise]() { promise->MaybeResolveWithUndefined(); },
 | 
						|
          [promise]() { promise->MaybeRejectWithUndefined(); });
 | 
						|
      break;
 | 
						|
    case nsIXULRuntime::PROCESS_TYPE_RDD:
 | 
						|
      RDDProcessManager::Get()->TestTriggerMetrics()->Then(
 | 
						|
          GetCurrentSerialEventTarget(), __func__,
 | 
						|
          [promise]() { promise->MaybeResolveWithUndefined(); },
 | 
						|
          [promise]() { promise->MaybeRejectWithUndefined(); });
 | 
						|
      break;
 | 
						|
    case nsIXULRuntime::PROCESS_TYPE_SOCKET:
 | 
						|
      Unused << net::SocketProcessParent::GetSingleton()
 | 
						|
                    ->SendTestTriggerMetrics()
 | 
						|
                    ->Then(
 | 
						|
                        GetCurrentSerialEventTarget(), __func__,
 | 
						|
                        [promise]() { promise->MaybeResolveWithUndefined(); },
 | 
						|
                        [promise]() { promise->MaybeRejectWithUndefined(); });
 | 
						|
      break;
 | 
						|
    case nsIXULRuntime::PROCESS_TYPE_UTILITY:
 | 
						|
      Unused << ipc::UtilityProcessManager::GetSingleton()
 | 
						|
                    ->GetProcessParent(ipc::SandboxingKind::GENERIC_UTILITY)
 | 
						|
                    ->SendTestTriggerMetrics()
 | 
						|
                    ->Then(
 | 
						|
                        GetCurrentSerialEventTarget(), __func__,
 | 
						|
                        [promise]() { promise->MaybeResolveWithUndefined(); },
 | 
						|
                        [promise]() { promise->MaybeRejectWithUndefined(); });
 | 
						|
      break;
 | 
						|
    default:
 | 
						|
      promise->MaybeRejectWithUndefined();
 | 
						|
      break;
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
}  // namespace mozilla::glean
 |