Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/toolkit/components/glean/ipc/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 22 kB image not shown  

Quelle  FOGIPC.cpp   Sprache: 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/ProcesstoolsMetrics.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/glean/bindings/jog/JOG.h"
#include "mozilla/glean/GleanMetrics.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"

#if defined(__APPLE__) && defined(__aarch64__)
#  define HAS_PROCESS_ENERGY
#endif

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;
  }
};

#ifdef HAS_PROCESS_ENERGY
struct ProcessEnergyMarker {
  static constexpr Span<const char> MarkerTypeName() {
    return MakeStringSpan("ProcessEnergy");
  }
  static void StreamJSONMarkerData(baseprofiler::SpliceableJSONWriter& aWriter,
                                   int64_t aUWh,
                                   const ProfilerString8View& aType) {
    aWriter.IntProperty("energy", aUWh);
    aWriter.StringProperty("label", aType);
  }
  static MarkerSchema MarkerTypeDisplay() {
    using MS = MarkerSchema;
    MS schema{MS::Location::MarkerChart, MS::Location::MarkerTable};
    schema.AddKeyLabelFormat("energy""Energy (µWh)", MS::Format::Integer);
    schema.SetTooltipLabel("{marker.name} - {marker.data.label}");
    schema.SetTableLabel(
        "{marker.name} - {marker.data.label}: {marker.data.energy}µWh");
    return schema;
  }
};
#endif

}  // 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";
  }
}

#ifdef HAS_PROCESS_ENERGY
static int64_t GetTaskEnergy() {
  task_power_info_v2_data_t task_power_info;
  mach_msg_type_number_t count = TASK_POWER_INFO_V2_COUNT;
  kern_return_t kr = task_info(mach_task_self(), TASK_POWER_INFO_V2,
                               (task_info_t)&task_power_info, &count);
  if (kr != KERN_SUCCESS) {
    return 0;
  }

  // task_energy is in nanojoules. We want microwatt-hours.
  return task_power_info.task_energy / 3.6 / 1e6;
}
#endif

void RecordPowerMetrics() {
  static uint64_t previousCpuTime = 0, previousGpuTime = 0;
#ifdef HAS_PROCESS_ENERGY
  static int64_t previousProcessEnergy = 0;
#endif

  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;
  }

#ifdef HAS_PROCESS_ENERGY
  int64_t processEnergy = GetTaskEnergy();
  int64_t newProcessEnergy = processEnergy - previousProcessEnergy;
#endif

  if (!newCpuTime && !newGpuTime
#ifdef HAS_PROCESS_ENERGY
      && newProcessEnergy <= 0
#endif
  ) {
    // 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;
          case hal::PROCESS_PRIORITY_PREALLOC:
            type.Assign("prealloc");
            gThisProcessType = ProcessType::eUnknown;
            break;
          default:
            MOZ_ASSERT_UNREACHABLE("Unsuppored process type for cpu time");
            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;
  }

#ifdef HAS_PROCESS_ENERGY
  if (newProcessEnergy) {
    power::energy_per_process_type.Get(type).Add(newProcessEnergy);
    PROFILER_MARKER("Process Energy", OTHER, {}, ProcessEnergyMarker,
                    newProcessEnergy, type);
    previousProcessEnergy += newProcessEnergy;
  }
#endif

  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 (RefPtr<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:
      mozilla::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: {
      RefPtr<net::SocketProcessParent> socketParent(
          net::SocketProcessParent::GetSingleton());
      Unused << socketParent->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

Messung V0.5
C=80 H=97 G=88

¤ Dauer der Verarbeitung: 0.2 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.