summaryrefslogtreecommitdiffstats
path: root/toolkit/components/telemetry/core/ipc
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/components/telemetry/core/ipc')
-rw-r--r--toolkit/components/telemetry/core/ipc/TelemetryComms.h400
-rw-r--r--toolkit/components/telemetry/core/ipc/TelemetryIPC.cpp59
-rw-r--r--toolkit/components/telemetry/core/ipc/TelemetryIPC.h114
-rw-r--r--toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp352
-rw-r--r--toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.h56
5 files changed, 981 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/core/ipc/TelemetryComms.h b/toolkit/components/telemetry/core/ipc/TelemetryComms.h
new file mode 100644
index 0000000000..75f59209b0
--- /dev/null
+++ b/toolkit/components/telemetry/core/ipc/TelemetryComms.h
@@ -0,0 +1,400 @@
+/* -*- 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/. */
+
+#ifndef Telemetry_Comms_h__
+#define Telemetry_Comms_h__
+
+#include "ipc/IPCMessageUtils.h"
+#include "ipc/IPCMessageUtilsSpecializations.h"
+#include "mozilla/Telemetry.h"
+#include "mozilla/TelemetryProcessEnums.h"
+#include "mozilla/TimeStamp.h"
+#include "mozilla/Variant.h"
+#include "nsITelemetry.h"
+
+namespace mozilla {
+namespace Telemetry {
+
+// Histogram accumulation types.
+enum HistogramID : uint32_t;
+
+struct HistogramAccumulation {
+ mozilla::Telemetry::HistogramID mId;
+ uint32_t mSample;
+};
+
+struct KeyedHistogramAccumulation {
+ mozilla::Telemetry::HistogramID mId;
+ uint32_t mSample;
+ nsCString mKey;
+};
+
+// Scalar accumulation types.
+enum class ScalarID : uint32_t;
+
+enum class ScalarActionType : uint32_t { eSet = 0, eAdd = 1, eSetMaximum = 2 };
+
+typedef mozilla::Variant<uint32_t, bool, nsString> ScalarVariant;
+
+struct ScalarAction {
+ uint32_t mId;
+ bool mDynamic;
+ ScalarActionType mActionType;
+ // We need to wrap mData in a Maybe otherwise the IPC system
+ // is unable to instantiate a ScalarAction.
+ Maybe<ScalarVariant> mData;
+ // The process type this scalar should be recorded for.
+ // The IPC system will determine the process this action was coming from
+ // later.
+ mozilla::Telemetry::ProcessID mProcessType;
+};
+
+struct KeyedScalarAction {
+ uint32_t mId;
+ bool mDynamic;
+ ScalarActionType mActionType;
+ nsCString mKey;
+ // We need to wrap mData in a Maybe otherwise the IPC system
+ // is unable to instantiate a ScalarAction.
+ Maybe<ScalarVariant> mData;
+ // The process type this scalar should be recorded for.
+ // The IPC system will determine the process this action was coming from
+ // later.
+ mozilla::Telemetry::ProcessID mProcessType;
+};
+
+// Dynamic scalars support.
+struct DynamicScalarDefinition {
+ uint32_t type;
+ uint32_t dataset;
+ bool expired;
+ bool keyed;
+ bool builtin;
+ nsCString name;
+
+ bool operator==(const DynamicScalarDefinition& rhs) const {
+ return type == rhs.type && dataset == rhs.dataset &&
+ expired == rhs.expired && keyed == rhs.keyed &&
+ builtin == rhs.builtin && name.Equals(rhs.name);
+ }
+};
+
+struct ChildEventData {
+ mozilla::TimeStamp timestamp;
+ nsCString category;
+ nsCString method;
+ nsCString object;
+ mozilla::Maybe<nsCString> value;
+ CopyableTArray<EventExtraEntry> extra;
+};
+
+struct DiscardedData {
+ uint32_t mDiscardedHistogramAccumulations;
+ uint32_t mDiscardedKeyedHistogramAccumulations;
+ uint32_t mDiscardedScalarActions;
+ uint32_t mDiscardedKeyedScalarActions;
+ uint32_t mDiscardedChildEvents;
+};
+
+} // namespace Telemetry
+} // namespace mozilla
+
+namespace IPC {
+
+template <>
+struct ParamTraits<mozilla::Telemetry::HistogramAccumulation> {
+ typedef mozilla::Telemetry::HistogramAccumulation paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ aWriter->WriteUInt32(aParam.mId);
+ WriteParam(aWriter, aParam.mSample);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!aReader->ReadUInt32(reinterpret_cast<uint32_t*>(&(aResult->mId))) ||
+ !ReadParam(aReader, &(aResult->mSample))) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::Telemetry::KeyedHistogramAccumulation> {
+ typedef mozilla::Telemetry::KeyedHistogramAccumulation paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ aWriter->WriteUInt32(aParam.mId);
+ WriteParam(aWriter, aParam.mSample);
+ WriteParam(aWriter, aParam.mKey);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!aReader->ReadUInt32(reinterpret_cast<uint32_t*>(&(aResult->mId))) ||
+ !ReadParam(aReader, &(aResult->mSample)) ||
+ !ReadParam(aReader, &(aResult->mKey))) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+/**
+ * IPC scalar data message serialization and de-serialization.
+ */
+template <>
+struct ParamTraits<mozilla::Telemetry::ScalarAction> {
+ typedef mozilla::Telemetry::ScalarAction paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ // Write the message type
+ aWriter->WriteUInt32(aParam.mId);
+ WriteParam(aWriter, aParam.mDynamic);
+ WriteParam(aWriter, static_cast<uint32_t>(aParam.mActionType));
+
+ if (aParam.mData.isNothing()) {
+ MOZ_CRASH("There is no data in the ScalarAction.");
+ return;
+ }
+
+ if (aParam.mData->is<uint32_t>()) {
+ // That's a nsITelemetry::SCALAR_TYPE_COUNT.
+ WriteParam(aWriter,
+ static_cast<uint32_t>(nsITelemetry::SCALAR_TYPE_COUNT));
+ WriteParam(aWriter, aParam.mData->as<uint32_t>());
+ } else if (aParam.mData->is<nsString>()) {
+ // That's a nsITelemetry::SCALAR_TYPE_STRING.
+ WriteParam(aWriter,
+ static_cast<uint32_t>(nsITelemetry::SCALAR_TYPE_STRING));
+ WriteParam(aWriter, aParam.mData->as<nsString>());
+ } else if (aParam.mData->is<bool>()) {
+ // That's a nsITelemetry::SCALAR_TYPE_BOOLEAN.
+ WriteParam(aWriter,
+ static_cast<uint32_t>(nsITelemetry::SCALAR_TYPE_BOOLEAN));
+ WriteParam(aWriter, aParam.mData->as<bool>());
+ } else {
+ MOZ_CRASH("Unknown scalar type.");
+ }
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ // Read the scalar ID and the scalar type.
+ uint32_t scalarType = 0;
+ if (!aReader->ReadUInt32(reinterpret_cast<uint32_t*>(&(aResult->mId))) ||
+ !ReadParam(aReader, reinterpret_cast<bool*>(&(aResult->mDynamic))) ||
+ !ReadParam(aReader,
+ reinterpret_cast<uint32_t*>(&(aResult->mActionType))) ||
+ !ReadParam(aReader, &scalarType)) {
+ return false;
+ }
+
+ // De-serialize the data based on the scalar type.
+ switch (scalarType) {
+ case nsITelemetry::SCALAR_TYPE_COUNT: {
+ uint32_t data = 0;
+ // De-serialize the data.
+ if (!ReadParam(aReader, &data)) {
+ return false;
+ }
+ aResult->mData = mozilla::Some(mozilla::AsVariant(data));
+ break;
+ }
+ case nsITelemetry::SCALAR_TYPE_STRING: {
+ nsString data;
+ // De-serialize the data.
+ if (!ReadParam(aReader, &data)) {
+ return false;
+ }
+ aResult->mData = mozilla::Some(mozilla::AsVariant(data));
+ break;
+ }
+ case nsITelemetry::SCALAR_TYPE_BOOLEAN: {
+ bool data = false;
+ // De-serialize the data.
+ if (!ReadParam(aReader, &data)) {
+ return false;
+ }
+ aResult->mData = mozilla::Some(mozilla::AsVariant(data));
+ break;
+ }
+ default:
+ MOZ_ASSERT(false, "Unknown scalar type.");
+ return false;
+ }
+
+ return true;
+ }
+};
+
+/**
+ * IPC keyed scalar data message serialization and de-serialization.
+ */
+template <>
+struct ParamTraits<mozilla::Telemetry::KeyedScalarAction> {
+ typedef mozilla::Telemetry::KeyedScalarAction paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ // Write the message type
+ aWriter->WriteUInt32(static_cast<uint32_t>(aParam.mId));
+ WriteParam(aWriter, aParam.mDynamic);
+ WriteParam(aWriter, static_cast<uint32_t>(aParam.mActionType));
+ WriteParam(aWriter, aParam.mKey);
+
+ if (aParam.mData.isNothing()) {
+ MOZ_CRASH("There is no data in the KeyedScalarAction.");
+ return;
+ }
+
+ if (aParam.mData->is<uint32_t>()) {
+ // That's a nsITelemetry::SCALAR_TYPE_COUNT.
+ WriteParam(aWriter,
+ static_cast<uint32_t>(nsITelemetry::SCALAR_TYPE_COUNT));
+ WriteParam(aWriter, aParam.mData->as<uint32_t>());
+ } else if (aParam.mData->is<nsString>()) {
+ // That's a nsITelemetry::SCALAR_TYPE_STRING.
+ // Keyed string scalars are not supported.
+ MOZ_ASSERT(false,
+ "Keyed String Scalar unable to be write from child process. "
+ "Not supported.");
+ } else if (aParam.mData->is<bool>()) {
+ // That's a nsITelemetry::SCALAR_TYPE_BOOLEAN.
+ WriteParam(aWriter,
+ static_cast<uint32_t>(nsITelemetry::SCALAR_TYPE_BOOLEAN));
+ WriteParam(aWriter, aParam.mData->as<bool>());
+ } else {
+ MOZ_CRASH("Unknown keyed scalar type.");
+ }
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ // Read the scalar ID and the scalar type.
+ uint32_t scalarType = 0;
+ if (!aReader->ReadUInt32(reinterpret_cast<uint32_t*>(&(aResult->mId))) ||
+ !ReadParam(aReader, reinterpret_cast<bool*>(&(aResult->mDynamic))) ||
+ !ReadParam(aReader,
+ reinterpret_cast<uint32_t*>(&(aResult->mActionType))) ||
+ !ReadParam(aReader, &(aResult->mKey)) ||
+ !ReadParam(aReader, &scalarType)) {
+ return false;
+ }
+
+ // De-serialize the data based on the scalar type.
+ switch (scalarType) {
+ case nsITelemetry::SCALAR_TYPE_COUNT: {
+ uint32_t data = 0;
+ // De-serialize the data.
+ if (!ReadParam(aReader, &data)) {
+ return false;
+ }
+ aResult->mData = mozilla::Some(mozilla::AsVariant(data));
+ break;
+ }
+ case nsITelemetry::SCALAR_TYPE_STRING: {
+ // Keyed string scalars are not supported.
+ MOZ_ASSERT(false,
+ "Keyed String Scalar unable to be read from child process. "
+ "Not supported.");
+ return false;
+ }
+ case nsITelemetry::SCALAR_TYPE_BOOLEAN: {
+ bool data = false;
+ // De-serialize the data.
+ if (!ReadParam(aReader, &data)) {
+ return false;
+ }
+ aResult->mData = mozilla::Some(mozilla::AsVariant(data));
+ break;
+ }
+ default:
+ MOZ_ASSERT(false, "Unknown keyed scalar type.");
+ return false;
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::Telemetry::DynamicScalarDefinition> {
+ typedef mozilla::Telemetry::DynamicScalarDefinition paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ nsCString name;
+ WriteParam(aWriter, aParam.type);
+ WriteParam(aWriter, aParam.dataset);
+ WriteParam(aWriter, aParam.expired);
+ WriteParam(aWriter, aParam.keyed);
+ WriteParam(aWriter, aParam.builtin);
+ WriteParam(aWriter, aParam.name);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!ReadParam(aReader, reinterpret_cast<uint32_t*>(&(aResult->type))) ||
+ !ReadParam(aReader, reinterpret_cast<uint32_t*>(&(aResult->dataset))) ||
+ !ReadParam(aReader, reinterpret_cast<bool*>(&(aResult->expired))) ||
+ !ReadParam(aReader, reinterpret_cast<bool*>(&(aResult->keyed))) ||
+ !ReadParam(aReader, reinterpret_cast<bool*>(&(aResult->builtin))) ||
+ !ReadParam(aReader, &(aResult->name))) {
+ return false;
+ }
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::Telemetry::ChildEventData> {
+ typedef mozilla::Telemetry::ChildEventData paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.timestamp);
+ WriteParam(aWriter, aParam.category);
+ WriteParam(aWriter, aParam.method);
+ WriteParam(aWriter, aParam.object);
+ WriteParam(aWriter, aParam.value);
+ WriteParam(aWriter, aParam.extra);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!ReadParam(aReader, &(aResult->timestamp)) ||
+ !ReadParam(aReader, &(aResult->category)) ||
+ !ReadParam(aReader, &(aResult->method)) ||
+ !ReadParam(aReader, &(aResult->object)) ||
+ !ReadParam(aReader, &(aResult->value)) ||
+ !ReadParam(aReader, &(aResult->extra))) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::Telemetry::EventExtraEntry> {
+ typedef mozilla::Telemetry::EventExtraEntry paramType;
+
+ static void Write(MessageWriter* aWriter, const paramType& aParam) {
+ WriteParam(aWriter, aParam.key);
+ WriteParam(aWriter, aParam.value);
+ }
+
+ static bool Read(MessageReader* aReader, paramType* aResult) {
+ if (!ReadParam(aReader, &(aResult->key)) ||
+ !ReadParam(aReader, &(aResult->value))) {
+ return false;
+ }
+
+ return true;
+ }
+};
+
+template <>
+struct ParamTraits<mozilla::Telemetry::DiscardedData>
+ : public PlainOldDataSerializer<mozilla::Telemetry::DiscardedData> {};
+
+} // namespace IPC
+
+#endif // Telemetry_Comms_h__
diff --git a/toolkit/components/telemetry/core/ipc/TelemetryIPC.cpp b/toolkit/components/telemetry/core/ipc/TelemetryIPC.cpp
new file mode 100644
index 0000000000..daeaeece65
--- /dev/null
+++ b/toolkit/components/telemetry/core/ipc/TelemetryIPC.cpp
@@ -0,0 +1,59 @@
+/* -*- 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 "TelemetryIPC.h"
+#include "../TelemetryScalar.h"
+#include "../TelemetryHistogram.h"
+#include "../TelemetryEvent.h"
+
+namespace mozilla {
+
+void TelemetryIPC::AccumulateChildHistograms(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::HistogramAccumulation>& aAccumulations) {
+ TelemetryHistogram::AccumulateChild(aProcessType, aAccumulations);
+}
+
+void TelemetryIPC::AccumulateChildKeyedHistograms(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::KeyedHistogramAccumulation>& aAccumulations) {
+ TelemetryHistogram::AccumulateChildKeyed(aProcessType, aAccumulations);
+}
+
+void TelemetryIPC::UpdateChildScalars(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::ScalarAction>& aScalarActions) {
+ TelemetryScalar::UpdateChildData(aProcessType, aScalarActions);
+}
+
+void TelemetryIPC::UpdateChildKeyedScalars(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::KeyedScalarAction>& aScalarActions) {
+ TelemetryScalar::UpdateChildKeyedData(aProcessType, aScalarActions);
+}
+
+void TelemetryIPC::GetDynamicScalarDefinitions(
+ nsTArray<mozilla::Telemetry::DynamicScalarDefinition>& aDefs) {
+ TelemetryScalar::GetDynamicScalarDefinitions(aDefs);
+}
+
+void TelemetryIPC::AddDynamicScalarDefinitions(
+ const nsTArray<mozilla::Telemetry::DynamicScalarDefinition>& aDefs) {
+ TelemetryScalar::AddDynamicScalarDefinitions(aDefs);
+}
+
+void TelemetryIPC::RecordChildEvents(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::ChildEventData>& aEvents) {
+ TelemetryEvent::RecordChildEvents(aProcessType, aEvents);
+}
+
+void TelemetryIPC::RecordDiscardedData(
+ Telemetry::ProcessID aProcessType,
+ const Telemetry::DiscardedData& aDiscardedData) {
+ TelemetryScalar::RecordDiscardedData(aProcessType, aDiscardedData);
+}
+} // namespace mozilla
diff --git a/toolkit/components/telemetry/core/ipc/TelemetryIPC.h b/toolkit/components/telemetry/core/ipc/TelemetryIPC.h
new file mode 100644
index 0000000000..bf45280059
--- /dev/null
+++ b/toolkit/components/telemetry/core/ipc/TelemetryIPC.h
@@ -0,0 +1,114 @@
+/* -*- 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/. */
+
+#ifndef TelemetryIPC_h__
+#define TelemetryIPC_h__
+
+#include "mozilla/TelemetryProcessEnums.h"
+#include "nsTArray.h"
+
+// This module provides the interface to accumulate Telemetry from child
+// processes. Top-level actors for different child processes types
+// (ContentParent, GPUChild) will call this for messages from their respective
+// processes.
+
+namespace mozilla {
+
+namespace Telemetry {
+
+struct HistogramAccumulation;
+struct KeyedHistogramAccumulation;
+struct ScalarAction;
+struct KeyedScalarAction;
+struct DynamicScalarDefinition;
+struct ChildEventData;
+struct DiscardedData;
+
+} // namespace Telemetry
+
+namespace TelemetryIPC {
+
+/**
+ * Accumulate child process data into histograms for the given process type.
+ *
+ * @param aProcessType - the process type to accumulate the histograms for
+ * @param aAccumulations - accumulation actions to perform
+ */
+void AccumulateChildHistograms(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::HistogramAccumulation>& aAccumulations);
+
+/**
+ * Accumulate child process data into keyed histograms for the given process
+ * type.
+ *
+ * @param aProcessType - the process type to accumulate the keyed histograms for
+ * @param aAccumulations - accumulation actions to perform
+ */
+void AccumulateChildKeyedHistograms(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::KeyedHistogramAccumulation>& aAccumulations);
+
+/**
+ * Update scalars for the given process type with the data coming from child
+ * process.
+ *
+ * @param aProcessType - the process type to process the scalar actions for
+ * @param aScalarActions - actions to update the scalar data
+ */
+void UpdateChildScalars(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::ScalarAction>& aScalarActions);
+
+/**
+ * Update keyed scalars for the given process type with the data coming from
+ * child process.
+ *
+ * @param aProcessType - the process type to process the keyed scalar actions
+ * for
+ * @param aScalarActions - actions to update the keyed scalar data
+ */
+void UpdateChildKeyedScalars(
+ Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::KeyedScalarAction>& aScalarActions);
+
+/**
+ * Record events for the given process type with the data coming from child
+ * process.
+ *
+ * @param aProcessType - the process type to record the events for
+ * @param aEvents - events to record
+ */
+void RecordChildEvents(Telemetry::ProcessID aProcessType,
+ const nsTArray<Telemetry::ChildEventData>& aEvents);
+
+/**
+ * Record the counts of data the child process had to discard
+ *
+ * @param aProcessType - the process reporting the discarded data
+ * @param aDiscardedData - stats about the discarded data
+ */
+void RecordDiscardedData(Telemetry::ProcessID aProcessType,
+ const Telemetry::DiscardedData& aDiscardedData);
+
+/**
+ * Get the dynamic scalar definitions from the parent process.
+ * @param aDefs - The array that will contain the scalar definitions.
+ */
+void GetDynamicScalarDefinitions(
+ nsTArray<mozilla::Telemetry::DynamicScalarDefinition>& aDefs);
+
+/**
+ * Add the dynamic scalar definitions coming from the parent process
+ * to the current child process.
+ * @param aDefs - The array that contains the scalar definitions.
+ */
+void AddDynamicScalarDefinitions(
+ const nsTArray<mozilla::Telemetry::DynamicScalarDefinition>& aDefs);
+
+} // namespace TelemetryIPC
+} // namespace mozilla
+
+#endif // TelemetryIPC_h__
diff --git a/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp b/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp
new file mode 100644
index 0000000000..f645edd27d
--- /dev/null
+++ b/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.cpp
@@ -0,0 +1,352 @@
+/* -*- 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 "TelemetryIPCAccumulator.h"
+
+#include "core/TelemetryScalar.h"
+#include "mozilla/TelemetryProcessEnums.h"
+#include "mozilla/dom/ContentChild.h"
+#include "mozilla/gfx/GPUParent.h"
+#include "mozilla/RDDParent.h"
+#include "mozilla/net/SocketProcessChild.h"
+#include "mozilla/ipc/UtilityProcessChild.h"
+#include "mozilla/SchedulerGroup.h"
+#include "mozilla/StaticMutex.h"
+#include "mozilla/StaticPrefs_toolkit.h"
+#include "mozilla/StaticPtr.h"
+#include "mozilla/Unused.h"
+#include "nsITimer.h"
+#include "nsThreadUtils.h"
+
+using mozilla::StaticAutoPtr;
+using mozilla::StaticMutex;
+using mozilla::StaticMutexAutoLock;
+using mozilla::TaskCategory;
+using mozilla::Telemetry::ChildEventData;
+using mozilla::Telemetry::DiscardedData;
+using mozilla::Telemetry::HistogramAccumulation;
+using mozilla::Telemetry::KeyedHistogramAccumulation;
+using mozilla::Telemetry::KeyedScalarAction;
+using mozilla::Telemetry::ScalarAction;
+using mozilla::Telemetry::ScalarActionType;
+using mozilla::Telemetry::ScalarVariant;
+
+namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
+
+// To stop growing unbounded in memory while waiting for
+// StaticPrefs::toolkit_telemetry_ipcBatchTimeout() milliseconds to drain the
+// probe accumulation arrays, we request an immediate flush if the arrays
+// manage to reach certain high water mark of elements.
+const size_t kHistogramAccumulationsArrayHighWaterMark = 5 * 1024;
+const size_t kScalarActionsArrayHighWaterMark = 10000;
+// With the current limits, events cost us about 1100 bytes each.
+// This limits memory use to about 10MB.
+const size_t kEventsArrayHighWaterMark = 10000;
+// If we are starved we can overshoot the watermark.
+// This is the multiplier over which we will discard data.
+const size_t kWaterMarkDiscardFactor = 5;
+
+// Counts of how many pieces of data we have discarded.
+DiscardedData gDiscardedData = {0};
+
+// This timer is used for batching and sending child process accumulations to
+// the parent.
+nsITimer* gIPCTimer = nullptr;
+mozilla::Atomic<bool, mozilla::Relaxed> gIPCTimerArmed(false);
+mozilla::Atomic<bool, mozilla::Relaxed> gIPCTimerArming(false);
+
+// This batches child process accumulations that should be sent to the parent.
+StaticAutoPtr<nsTArray<HistogramAccumulation>> gHistogramAccumulations;
+StaticAutoPtr<nsTArray<KeyedHistogramAccumulation>>
+ gKeyedHistogramAccumulations;
+StaticAutoPtr<nsTArray<ScalarAction>> gChildScalarsActions;
+StaticAutoPtr<nsTArray<KeyedScalarAction>> gChildKeyedScalarsActions;
+StaticAutoPtr<nsTArray<ChildEventData>> gChildEvents;
+
+// This is a StaticMutex rather than a plain Mutex so that (1)
+// it gets initialised in a thread-safe manner the first time
+// it is used, and (2) because it is never de-initialised, and
+// a normal Mutex would show up as a leak in BloatView. StaticMutex
+// also has the "OffTheBooks" property, so it won't show as a leak
+// in BloatView.
+static StaticMutex gTelemetryIPCAccumulatorMutex MOZ_UNANNOTATED;
+
+namespace {
+
+void DoArmIPCTimerMainThread(const StaticMutexAutoLock& lock) {
+ MOZ_ASSERT(NS_IsMainThread());
+ gIPCTimerArming = false;
+ if (gIPCTimerArmed) {
+ return;
+ }
+ if (!gIPCTimer) {
+ gIPCTimer = NS_NewTimer().take();
+ }
+ if (gIPCTimer) {
+ gIPCTimer->InitWithNamedFuncCallback(
+ TelemetryIPCAccumulator::IPCTimerFired, nullptr,
+ mozilla::StaticPrefs::toolkit_telemetry_ipcBatchTimeout(),
+ nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
+ "TelemetryIPCAccumulator::IPCTimerFired");
+ gIPCTimerArmed = true;
+ }
+}
+
+void ArmIPCTimer(const StaticMutexAutoLock& lock) {
+ if (gIPCTimerArmed || gIPCTimerArming) {
+ return;
+ }
+ gIPCTimerArming = true;
+ if (NS_IsMainThread()) {
+ DoArmIPCTimerMainThread(lock);
+ } else {
+ TelemetryIPCAccumulator::DispatchToMainThread(NS_NewRunnableFunction(
+ "TelemetryIPCAccumulator::ArmIPCTimer", []() -> void {
+ StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
+ DoArmIPCTimerMainThread(locker);
+ }));
+ }
+}
+
+void DispatchIPCTimerFired() {
+ TelemetryIPCAccumulator::DispatchToMainThread(NS_NewRunnableFunction(
+ "TelemetryIPCAccumulator::IPCTimerFired", []() -> void {
+ TelemetryIPCAccumulator::IPCTimerFired(nullptr, nullptr);
+ }));
+}
+
+} // anonymous namespace
+
+////////////////////////////////////////////////////////////////////////
+////////////////////////////////////////////////////////////////////////
+//
+// EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryIPCAccumulator::
+
+void TelemetryIPCAccumulator::AccumulateChildHistogram(
+ mozilla::Telemetry::HistogramID aId, uint32_t aSample) {
+ StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
+ if (!gHistogramAccumulations) {
+ gHistogramAccumulations = new nsTArray<HistogramAccumulation>();
+ }
+ if (gHistogramAccumulations->Length() >=
+ kWaterMarkDiscardFactor * kHistogramAccumulationsArrayHighWaterMark) {
+ gDiscardedData.mDiscardedHistogramAccumulations++;
+ return;
+ }
+ if (gHistogramAccumulations->Length() ==
+ kHistogramAccumulationsArrayHighWaterMark) {
+ DispatchIPCTimerFired();
+ }
+ gHistogramAccumulations->AppendElement(HistogramAccumulation{aId, aSample});
+ ArmIPCTimer(locker);
+}
+
+void TelemetryIPCAccumulator::AccumulateChildKeyedHistogram(
+ mozilla::Telemetry::HistogramID aId, const nsCString& aKey,
+ uint32_t aSample) {
+ StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
+ if (!gKeyedHistogramAccumulations) {
+ gKeyedHistogramAccumulations = new nsTArray<KeyedHistogramAccumulation>();
+ }
+ if (gKeyedHistogramAccumulations->Length() >=
+ kWaterMarkDiscardFactor * kHistogramAccumulationsArrayHighWaterMark) {
+ gDiscardedData.mDiscardedKeyedHistogramAccumulations++;
+ return;
+ }
+ if (gKeyedHistogramAccumulations->Length() ==
+ kHistogramAccumulationsArrayHighWaterMark) {
+ DispatchIPCTimerFired();
+ }
+ gKeyedHistogramAccumulations->AppendElement(
+ KeyedHistogramAccumulation{aId, aSample, aKey});
+ ArmIPCTimer(locker);
+}
+
+void TelemetryIPCAccumulator::RecordChildScalarAction(
+ uint32_t aId, bool aDynamic, ScalarActionType aAction,
+ const ScalarVariant& aValue) {
+ StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
+ // Make sure to have the storage.
+ if (!gChildScalarsActions) {
+ gChildScalarsActions = new nsTArray<ScalarAction>();
+ }
+ if (gChildScalarsActions->Length() >=
+ kWaterMarkDiscardFactor * kScalarActionsArrayHighWaterMark) {
+ gDiscardedData.mDiscardedScalarActions++;
+ return;
+ }
+ if (gChildScalarsActions->Length() == kScalarActionsArrayHighWaterMark) {
+ DispatchIPCTimerFired();
+ }
+ // Store the action. The ProcessID will be determined by the receiver.
+ gChildScalarsActions->AppendElement(ScalarAction{
+ aId, aDynamic, aAction, Some(aValue), Telemetry::ProcessID::Count});
+ ArmIPCTimer(locker);
+}
+
+void TelemetryIPCAccumulator::RecordChildKeyedScalarAction(
+ uint32_t aId, bool aDynamic, const nsAString& aKey,
+ ScalarActionType aAction, const ScalarVariant& aValue) {
+ StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
+ // Make sure to have the storage.
+ if (!gChildKeyedScalarsActions) {
+ gChildKeyedScalarsActions = new nsTArray<KeyedScalarAction>();
+ }
+ if (gChildKeyedScalarsActions->Length() >=
+ kWaterMarkDiscardFactor * kScalarActionsArrayHighWaterMark) {
+ gDiscardedData.mDiscardedKeyedScalarActions++;
+ return;
+ }
+ if (gChildKeyedScalarsActions->Length() == kScalarActionsArrayHighWaterMark) {
+ DispatchIPCTimerFired();
+ }
+ // Store the action. The ProcessID will be determined by the receiver.
+ gChildKeyedScalarsActions->AppendElement(
+ KeyedScalarAction{aId, aDynamic, aAction, NS_ConvertUTF16toUTF8(aKey),
+ Some(aValue), Telemetry::ProcessID::Count});
+ ArmIPCTimer(locker);
+}
+
+void TelemetryIPCAccumulator::RecordChildEvent(
+ const mozilla::TimeStamp& timestamp, const nsACString& category,
+ const nsACString& method, const nsACString& object,
+ const mozilla::Maybe<nsCString>& value,
+ const nsTArray<mozilla::Telemetry::EventExtraEntry>& extra) {
+ StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
+
+ if (!gChildEvents) {
+ gChildEvents = new nsTArray<ChildEventData>();
+ }
+
+ if (gChildEvents->Length() >=
+ kWaterMarkDiscardFactor * kEventsArrayHighWaterMark) {
+ gDiscardedData.mDiscardedChildEvents++;
+ return;
+ }
+
+ if (gChildEvents->Length() == kEventsArrayHighWaterMark) {
+ DispatchIPCTimerFired();
+ }
+
+ // Store the event.
+ gChildEvents->AppendElement(
+ ChildEventData{timestamp, nsCString(category), nsCString(method),
+ nsCString(object), value, extra.Clone()});
+ ArmIPCTimer(locker);
+}
+
+// This method takes the lock only to double-buffer the batched telemetry.
+// It releases the lock before calling out to IPC code which can (and does)
+// Accumulate (which would deadlock)
+template <class TActor>
+static void SendAccumulatedData(TActor* ipcActor) {
+ // Get the accumulated data and free the storage buffers.
+ nsTArray<HistogramAccumulation> histogramsToSend;
+ nsTArray<KeyedHistogramAccumulation> keyedHistogramsToSend;
+ nsTArray<ScalarAction> scalarsToSend;
+ nsTArray<KeyedScalarAction> keyedScalarsToSend;
+ nsTArray<ChildEventData> eventsToSend;
+ DiscardedData discardedData;
+
+ {
+ StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
+ if (gHistogramAccumulations) {
+ histogramsToSend = std::move(*gHistogramAccumulations);
+ }
+ if (gKeyedHistogramAccumulations) {
+ keyedHistogramsToSend = std::move(*gKeyedHistogramAccumulations);
+ }
+ if (gChildScalarsActions) {
+ scalarsToSend = std::move(*gChildScalarsActions);
+ }
+ if (gChildKeyedScalarsActions) {
+ keyedScalarsToSend = std::move(*gChildKeyedScalarsActions);
+ }
+ if (gChildEvents) {
+ eventsToSend = std::move(*gChildEvents);
+ }
+ discardedData = gDiscardedData;
+ gDiscardedData = {0};
+ }
+
+ // Send the accumulated data to the parent process.
+ MOZ_ASSERT(ipcActor);
+ if (histogramsToSend.Length()) {
+ mozilla::Unused << NS_WARN_IF(
+ !ipcActor->SendAccumulateChildHistograms(histogramsToSend));
+ }
+ if (keyedHistogramsToSend.Length()) {
+ mozilla::Unused << NS_WARN_IF(
+ !ipcActor->SendAccumulateChildKeyedHistograms(keyedHistogramsToSend));
+ }
+ if (scalarsToSend.Length()) {
+ mozilla::Unused << NS_WARN_IF(
+ !ipcActor->SendUpdateChildScalars(scalarsToSend));
+ }
+ if (keyedScalarsToSend.Length()) {
+ mozilla::Unused << NS_WARN_IF(
+ !ipcActor->SendUpdateChildKeyedScalars(keyedScalarsToSend));
+ }
+ if (eventsToSend.Length()) {
+ mozilla::Unused << NS_WARN_IF(
+ !ipcActor->SendRecordChildEvents(eventsToSend));
+ }
+ mozilla::Unused << NS_WARN_IF(
+ !ipcActor->SendRecordDiscardedData(discardedData));
+}
+
+// To ensure we don't loop IPCTimerFired->AccumulateChild->arm timer, we don't
+// unset gIPCTimerArmed until the IPC completes
+//
+// This function must be called on the main thread, otherwise IPC will fail.
+void TelemetryIPCAccumulator::IPCTimerFired(nsITimer* aTimer, void* aClosure) {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ // Send accumulated data to the correct parent process.
+ switch (XRE_GetProcessType()) {
+ case GeckoProcessType_Content:
+ SendAccumulatedData(mozilla::dom::ContentChild::GetSingleton());
+ break;
+ case GeckoProcessType_GPU:
+ SendAccumulatedData(mozilla::gfx::GPUParent::GetSingleton());
+ break;
+ case GeckoProcessType_RDD:
+ SendAccumulatedData(mozilla::RDDParent::GetSingleton());
+ break;
+ case GeckoProcessType_Socket:
+ SendAccumulatedData(mozilla::net::SocketProcessChild::GetSingleton());
+ break;
+ case GeckoProcessType_Utility:
+ SendAccumulatedData(
+ mozilla::ipc::UtilityProcessChild::GetSingleton().get());
+ break;
+ default:
+ MOZ_ASSERT_UNREACHABLE("Unsupported process type");
+ break;
+ }
+
+ gIPCTimerArmed = false;
+}
+
+void TelemetryIPCAccumulator::DeInitializeGlobalState() {
+ MOZ_ASSERT(NS_IsMainThread());
+
+ StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
+ if (gIPCTimer) {
+ NS_RELEASE(gIPCTimer);
+ }
+
+ gHistogramAccumulations = nullptr;
+ gKeyedHistogramAccumulations = nullptr;
+ gChildScalarsActions = nullptr;
+ gChildKeyedScalarsActions = nullptr;
+ gChildEvents = nullptr;
+}
+
+void TelemetryIPCAccumulator::DispatchToMainThread(
+ already_AddRefed<nsIRunnable>&& aEvent) {
+ SchedulerGroup::Dispatch(TaskCategory::Other, std::move(aEvent));
+}
diff --git a/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.h b/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.h
new file mode 100644
index 0000000000..e77ca95391
--- /dev/null
+++ b/toolkit/components/telemetry/core/ipc/TelemetryIPCAccumulator.h
@@ -0,0 +1,56 @@
+/* -*- 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/. */
+
+#ifndef TelemetryIPCAccumulator_h__
+#define TelemetryIPCAccumulator_h__
+
+#include "mozilla/AlreadyAddRefed.h"
+#include "mozilla/Maybe.h"
+#include "nsStringFwd.h"
+#include "mozilla/Telemetry.h" // for EventExtraEntry
+#include "mozilla/TelemetryComms.h" // for ScalarActionType, Scala...
+#include "mozilla/TelemetryHistogramEnums.h" // for HistogramID
+
+class nsIRunnable;
+class nsITimer;
+
+namespace mozilla {
+
+class TimeStamp;
+
+namespace TelemetryIPCAccumulator {
+
+// Histogram accumulation functions.
+void AccumulateChildHistogram(mozilla::Telemetry::HistogramID aId,
+ uint32_t aSample);
+void AccumulateChildKeyedHistogram(mozilla::Telemetry::HistogramID aId,
+ const nsCString& aKey, uint32_t aSample);
+
+// Scalar accumulation functions.
+void RecordChildScalarAction(uint32_t aId, bool aDynamic,
+ mozilla::Telemetry::ScalarActionType aAction,
+ const mozilla::Telemetry::ScalarVariant& aValue);
+
+void RecordChildKeyedScalarAction(
+ uint32_t aId, bool aDynamic, const nsAString& aKey,
+ mozilla::Telemetry::ScalarActionType aAction,
+ const mozilla::Telemetry::ScalarVariant& aValue);
+
+void RecordChildEvent(
+ const mozilla::TimeStamp& timestamp, const nsACString& category,
+ const nsACString& method, const nsACString& object,
+ const mozilla::Maybe<nsCString>& value,
+ const nsTArray<mozilla::Telemetry::EventExtraEntry>& extra);
+
+void IPCTimerFired(nsITimer* aTimer, void* aClosure);
+
+void DeInitializeGlobalState();
+
+void DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent);
+
+} // namespace TelemetryIPCAccumulator
+} // namespace mozilla
+
+#endif // TelemetryIPCAccumulator_h__