bug 1369041 - Allow child processes to discard data when overwhelmed r?gfritzsche f?bsmedberg draft
authorChris H-C <chutten@mozilla.com>
Fri, 09 Jun 2017 09:53:42 -0400
changeset 598201 02fe416f23f5302befa3cfa5e0435bb773598a7b
parent 594944 75be6742abb94d79f3bcb731ab7fafa3d42ac4da
child 634433 69912f57d2c5e4f2fac6181e22e10b98c432b439
push id65158
push userbmo:chutten@mozilla.com
push dateWed, 21 Jun 2017 14:50:55 +0000
reviewersgfritzsche
bugs1369041
milestone56.0a1
bug 1369041 - Allow child processes to discard data when overwhelmed r?gfritzsche f?bsmedberg It is possible in extreme cases that the Telemetry IPC Accumulator might be starved long enough that it cannot drain its stored accumulations for a while. Once we hit 5x the high water mark, start discarding data. Count each piece and type of discarded data and report it via a custom IPC message. MozReview-Commit-ID: JayRpa5QPec
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
gfx/ipc/GPUChild.cpp
gfx/ipc/GPUChild.h
gfx/ipc/PGPU.ipdl
toolkit/components/telemetry/Scalars.yaml
toolkit/components/telemetry/TelemetryScalar.cpp
toolkit/components/telemetry/TelemetryScalar.h
toolkit/components/telemetry/ipc/TelemetryComms.h
toolkit/components/telemetry/ipc/TelemetryIPC.cpp
toolkit/components/telemetry/ipc/TelemetryIPC.h
toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.cpp
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -5189,16 +5189,25 @@ ContentParent::RecvUpdateChildKeyedScala
 
 mozilla::ipc::IPCResult
 ContentParent::RecvRecordChildEvents(nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents)
 {
   TelemetryIPC::RecordChildEvents(GetTelemetryProcessID(mRemoteType), aEvents);
   return IPC_OK();
 }
 
+mozilla::ipc::IPCResult
+ContentParent::RecvRecordDiscardedData(
+                const mozilla::Telemetry::DiscardedData& aDiscardedData)
+{
+  TelemetryIPC::RecordDiscardedData(GetTelemetryProcessID(mRemoteType),
+                                    aDiscardedData);
+  return IPC_OK();
+}
+
 //////////////////////////////////////////////////////////////////
 // PURLClassifierParent
 
 PURLClassifierParent*
 ContentParent::AllocPURLClassifierParent(const Principal& aPrincipal,
                                          const bool& aUseTrackingProtection,
                                          bool* aSuccess)
 {
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -1169,16 +1169,18 @@ private:
   virtual mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms(
     InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
   virtual mozilla::ipc::IPCResult RecvUpdateChildScalars(
     InfallibleTArray<ScalarAction>&& aScalarActions) override;
   virtual mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(
     InfallibleTArray<KeyedScalarAction>&& aScalarActions) override;
   virtual mozilla::ipc::IPCResult RecvRecordChildEvents(
     nsTArray<ChildEventData>&& events) override;
+  virtual mozilla::ipc::IPCResult RecvRecordDiscardedData(
+    const DiscardedData& aDiscardedData) override;
 public:
   void SendGetFilesResponseAndForget(const nsID& aID,
                                      const GetFilesResponseResult& aResult);
 
   bool SendRequestMemoryReport(const uint32_t& aGeneration,
                                const bool& aAnonymize,
                                const bool& aMinimizeMemoryUsage,
                                const MaybeFileDesc& aDMDFile) override;
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -90,16 +90,17 @@ using mozilla::OriginAttributes from "mo
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositorOptions from "mozilla/layers/CompositorOptions.h";
 using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h";
 using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
 using mozilla::CrossProcessMutexHandle from "mozilla/ipc/CrossProcessMutex.h";
 
 union ChromeRegistryItem
 {
     ChromePackage;
     OverrideMapping;
     SubstitutionMapping;
 };
@@ -1083,16 +1084,17 @@ parent:
     /**
      * Messages for communicating child Telemetry to the parent process
      */
     async AccumulateChildHistograms(Accumulation[] accumulations);
     async AccumulateChildKeyedHistograms(KeyedAccumulation[] accumulations);
     async UpdateChildScalars(ScalarAction[] updates);
     async UpdateChildKeyedScalars(KeyedScalarAction[] updates);
     async RecordChildEvents(ChildEventData[] events);
+    async RecordDiscardedData(DiscardedData data);
 
     sync GetA11yContentId() returns (uint32_t aContentId);
     async A11yHandlerControl(uint32_t aPid,
                              IHandlerControlHolder aHandlerControl);
 
     async AddMemoryReport(MemoryReport aReport);
     async FinishMemoryReport(uint32_t aGeneration);
 
--- a/gfx/ipc/GPUChild.cpp
+++ b/gfx/ipc/GPUChild.cpp
@@ -194,16 +194,23 @@ GPUChild::RecvUpdateChildKeyedScalars(In
 mozilla::ipc::IPCResult
 GPUChild::RecvRecordChildEvents(nsTArray<mozilla::Telemetry::ChildEventData>&& aEvents)
 {
   TelemetryIPC::RecordChildEvents(Telemetry::ProcessID::Gpu, aEvents);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+GPUChild::RecvRecordDiscardedData(const mozilla::Telemetry::DiscardedData& aDiscardedData)
+{
+  TelemetryIPC::RecordDiscardedData(Telemetry::ProcessID::Gpu, aDiscardedData);
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 GPUChild::RecvNotifyDeviceReset(const GPUDeviceData& aData)
 {
   gfxPlatform::GetPlatform()->ImportGPUDeviceData(aData);
   mHost->mListener->OnProcessDeviceReset(mHost);
   return IPC_OK();
 }
 
 bool
--- a/gfx/ipc/GPUChild.h
+++ b/gfx/ipc/GPUChild.h
@@ -45,16 +45,17 @@ public:
   mozilla::ipc::IPCResult RecvReportCheckerboard(const uint32_t& aSeverity, const nsCString& aLog) override;
   mozilla::ipc::IPCResult RecvInitCrashReporter(Shmem&& shmem, const NativeThreadId& aThreadId) override;
 
   mozilla::ipc::IPCResult RecvAccumulateChildHistograms(InfallibleTArray<Accumulation>&& aAccumulations) override;
   mozilla::ipc::IPCResult RecvAccumulateChildKeyedHistograms(InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
   mozilla::ipc::IPCResult RecvUpdateChildScalars(InfallibleTArray<ScalarAction>&& aScalarActions) override;
   mozilla::ipc::IPCResult RecvUpdateChildKeyedScalars(InfallibleTArray<KeyedScalarAction>&& aScalarActions) override;
   mozilla::ipc::IPCResult RecvRecordChildEvents(nsTArray<ChildEventData>&& events) override;
+  mozilla::ipc::IPCResult RecvRecordDiscardedData(const DiscardedData& aDiscardedData) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override;
   mozilla::ipc::IPCResult RecvGraphicsError(const nsCString& aError) override;
   mozilla::ipc::IPCResult RecvNotifyUiObservers(const nsCString& aTopic) override;
   mozilla::ipc::IPCResult RecvNotifyDeviceReset(const GPUDeviceData& aData) override;
   mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
   mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
 
--- a/gfx/ipc/PGPU.ipdl
+++ b/gfx/ipc/PGPU.ipdl
@@ -15,16 +15,17 @@ include protocol PVideoDecoderManager;
 
 using base::ProcessId from "base/process.h";
 using mozilla::dom::NativeThreadId from "mozilla/dom/TabMessageUtils.h";
 using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::KeyedScalarAction from "mozilla/TelemetryComms.h";
 using mozilla::Telemetry::ChildEventData from "mozilla/TelemetryComms.h";
+using mozilla::Telemetry::DiscardedData from "mozilla/TelemetryComms.h";
 
 namespace mozilla {
 namespace gfx {
 
 union GfxPrefValue {
   bool;
   int32_t;
   uint32_t;
@@ -103,16 +104,17 @@ child:
   async NotifyUiObservers(nsCString aTopic);
 
   // Messages for reporting telemetry to the UI process.
   async AccumulateChildHistograms(Accumulation[] accumulations);
   async AccumulateChildKeyedHistograms(KeyedAccumulation[] accumulations);
   async UpdateChildScalars(ScalarAction[] actions);
   async UpdateChildKeyedScalars(KeyedScalarAction[] actions);
   async RecordChildEvents(ChildEventData[] events);
+  async RecordDiscardedData(DiscardedData data);
 
   async NotifyDeviceReset(GPUDeviceData status);
 
   async AddMemoryReport(MemoryReport aReport);
   async FinishMemoryReport(uint32_t aGeneration);
 };
 
 } // namespace gfx
--- a/toolkit/components/telemetry/Scalars.yaml
+++ b/toolkit/components/telemetry/Scalars.yaml
@@ -499,16 +499,78 @@ telemetry:
     expires: "58"
     kind: boolean
     notification_emails:
       - telemetry-client-dev@mozilla.com
     release_channel_collection: opt-out
     record_in_processes:
       - 'main'
 
+telemetry.discarded:
+  accumulations:
+    bug_numbers:
+      - 1369041
+    description: >
+      Number of discarded accumulations to histograms in child processes
+    expires: "never"
+    kind: uint
+    notification_emails:
+      - telemetry-client-dev@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'all_childs'
+  keyed_accumulations:
+    bug_numbers:
+      - 1369041
+    description: >
+      Number of discarded accumulations to keyed histograms in child processes
+    expires: "never"
+    kind: uint
+    notification_emails:
+      - telemetry-client-dev@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'all_childs'
+  scalar_actions:
+    bug_numbers:
+      - 1369041
+    description: >
+      Number of discarded actions on scalars in child processes
+    expires: "never"
+    kind: uint
+    notification_emails:
+      - telemetry-client-dev@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'all_childs'
+  keyed_scalar_actions:
+    bug_numbers:
+      - 1369041
+    description: >
+      Number of discarded actions on keyed scalars in child processes
+    expires: "never"
+    kind: uint
+    notification_emails:
+      - telemetry-client-dev@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'all_childs'
+  child_events:
+    bug_numbers:
+      - 1369041
+    description: >
+      Number of discarded events in child processes
+    expires: "never"
+    kind: uint
+    notification_emails:
+      - telemetry-client-dev@mozilla.com
+    release_channel_collection: opt-out
+    record_in_processes:
+      - 'all_childs'
+
 # The following section is for probes testing the Telemetry system. They will not be
 # submitted in pings and are only used for testing.
 telemetry.test:
   unsigned_int_kind:
     bug_numbers:
       - 1276190
     description: >
       This is a test uint type with a really long description, maybe spanning even multiple
--- a/toolkit/components/telemetry/TelemetryScalar.cpp
+++ b/toolkit/components/telemetry/TelemetryScalar.cpp
@@ -27,16 +27,17 @@ using mozilla::StaticMutex;
 using mozilla::StaticMutexAutoLock;
 using mozilla::Telemetry::Common::AutoHashtable;
 using mozilla::Telemetry::Common::IsExpiredVersion;
 using mozilla::Telemetry::Common::CanRecordDataset;
 using mozilla::Telemetry::Common::IsInDataset;
 using mozilla::Telemetry::Common::LogToBrowserConsole;
 using mozilla::Telemetry::Common::GetNameForProcessID;
 using mozilla::Telemetry::ScalarActionType;
+using mozilla::Telemetry::ScalarID;
 using mozilla::Telemetry::ScalarVariant;
 using mozilla::Telemetry::ProcessID;
 
 namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
 
 ////////////////////////////////////////////////////////////////////////
 ////////////////////////////////////////////////////////////////////////
 //
@@ -2322,8 +2323,48 @@ TelemetryScalar::UpdateChildKeyedData(Pr
           scalar->SetMaximum(NS_ConvertUTF8toUTF16(upd.mKey), upd.mData->as<uint32_t>());
           break;
         }
       default:
         NS_WARNING("Unsupported action coming from keyed scalar child updates.");
     }
   }
 }
+
+void
+TelemetryScalar::RecordDiscardedData(ProcessID aProcessType,
+                                     const mozilla::Telemetry::DiscardedData& aDiscardedData)
+{
+  MOZ_ASSERT(XRE_IsParentProcess(),
+             "Discarded Data must be updated from the parent process.");
+  StaticMutexAutoLock locker(gTelemetryScalarsMutex);
+  if (!internal_CanRecordBase()) {
+    return;
+  }
+
+  ScalarBase* scalar = nullptr;
+  mozilla::DebugOnly<nsresult> rv;
+
+  rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_ACCUMULATIONS,
+                                aProcessType, &scalar);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  scalar->AddValue(aDiscardedData.mDiscardedAccumulations);
+
+  rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_KEYED_ACCUMULATIONS,
+                                aProcessType, &scalar);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  scalar->AddValue(aDiscardedData.mDiscardedKeyedAccumulations);
+
+  rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_SCALAR_ACTIONS,
+                                aProcessType, &scalar);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  scalar->AddValue(aDiscardedData.mDiscardedScalarActions);
+
+  rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_KEYED_SCALAR_ACTIONS,
+                                aProcessType, &scalar);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  scalar->AddValue(aDiscardedData.mDiscardedKeyedScalarActions);
+
+  rv = internal_GetScalarByEnum(ScalarID::TELEMETRY_DISCARDED_CHILD_EVENTS,
+                                aProcessType, &scalar);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  scalar->AddValue(aDiscardedData.mDiscardedChildEvents);
+}
--- a/toolkit/components/telemetry/TelemetryScalar.h
+++ b/toolkit/components/telemetry/TelemetryScalar.h
@@ -62,11 +62,14 @@ size_t GetMapShallowSizesOfExcludingThis
 size_t GetScalarSizesOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf);
 
 void UpdateChildData(mozilla::Telemetry::ProcessID aProcessType,
                      const nsTArray<mozilla::Telemetry::ScalarAction>& aScalarActions);
 
 void UpdateChildKeyedData(mozilla::Telemetry::ProcessID aProcessType,
                           const nsTArray<mozilla::Telemetry::KeyedScalarAction>& aScalarActions);
 
+void RecordDiscardedData(mozilla::Telemetry::ProcessID aProcessType,
+                         const mozilla::Telemetry::DiscardedData& aDiscardedData);
+
 } // namespace TelemetryScalar
 
 #endif // TelemetryScalar_h__
--- a/toolkit/components/telemetry/ipc/TelemetryComms.h
+++ b/toolkit/components/telemetry/ipc/TelemetryComms.h
@@ -69,16 +69,24 @@ struct ChildEventData {
   mozilla::TimeStamp timestamp;
   nsCString category;
   nsCString method;
   nsCString object;
   mozilla::Maybe<nsCString> value;
   nsTArray<EventExtraEntry> extra;
 };
 
+struct DiscardedData {
+  uint32_t mDiscardedAccumulations;
+  uint32_t mDiscardedKeyedAccumulations;
+  uint32_t mDiscardedScalarActions;
+  uint32_t mDiscardedKeyedScalarActions;
+  uint32_t mDiscardedChildEvents;
+};
+
 } // namespace Telemetry
 } // namespace mozilla
 
 namespace IPC {
 
 template<>
 struct
 ParamTraits<mozilla::Telemetry::Accumulation>
@@ -352,11 +360,17 @@ ParamTraits<mozilla::Telemetry::EventExt
         !ReadParam(aMsg, aIter, &(aResult->value))) {
       return false;
     }
 
     return true;
   }
 };
 
+template<>
+struct
+ParamTraits<mozilla::Telemetry::DiscardedData>
+  : public PlainOldDataSerializer<mozilla::Telemetry::DiscardedData>
+{ };
+
 } // namespace IPC
 
 #endif // Telemetry_Comms_h__
--- a/toolkit/components/telemetry/ipc/TelemetryIPC.cpp
+++ b/toolkit/components/telemetry/ipc/TelemetryIPC.cpp
@@ -40,9 +40,15 @@ TelemetryIPC::UpdateChildKeyedScalars(Te
 }
 
 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);
 }
+}
--- a/toolkit/components/telemetry/ipc/TelemetryIPC.h
+++ b/toolkit/components/telemetry/ipc/TelemetryIPC.h
@@ -17,16 +17,17 @@
 namespace mozilla {
 namespace Telemetry {
 
 struct Accumulation;
 struct KeyedAccumulation;
 struct ScalarAction;
 struct KeyedScalarAction;
 struct ChildEventData;
+struct DiscardedData;
 
 }
 
 namespace TelemetryIPC {
 
 /**
  * Accumulate child process data into histograms for the given process type.
  *
@@ -67,12 +68,21 @@ void UpdateChildKeyedScalars(Telemetry::
  * 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);
+
 }
 }
 
 #endif // TelemetryIPC_h__
--- a/toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.cpp
+++ b/toolkit/components/telemetry/ipc/TelemetryIPCAccumulator.cpp
@@ -20,16 +20,17 @@
 #include "TelemetryScalar.h"
 
 using mozilla::StaticMutex;
 using mozilla::StaticMutexAutoLock;
 using mozilla::StaticAutoPtr;
 using mozilla::SystemGroup;
 using mozilla::TaskCategory;
 using mozilla::Telemetry::Accumulation;
+using mozilla::Telemetry::DiscardedData;
 using mozilla::Telemetry::KeyedAccumulation;
 using mozilla::Telemetry::ScalarActionType;
 using mozilla::Telemetry::ScalarAction;
 using mozilla::Telemetry::KeyedScalarAction;
 using mozilla::Telemetry::ScalarVariant;
 using mozilla::Telemetry::ChildEventData;
 
 namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
@@ -43,16 +44,22 @@ const uint32_t kBatchTimeoutMs = 2000;
 // To stop growing unbounded in memory while waiting for kBatchTimeoutMs 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<Accumulation>> gHistogramAccumulations;
@@ -133,47 +140,62 @@ DispatchIPCTimerFired()
 void
 TelemetryIPCAccumulator::AccumulateChildHistogram(mozilla::Telemetry::HistogramID aId,
                                                   uint32_t aSample)
 {
   StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
   if (!gHistogramAccumulations) {
     gHistogramAccumulations = new nsTArray<Accumulation>();
   }
+  if (gHistogramAccumulations->Length() >=
+      kWaterMarkDiscardFactor * kHistogramAccumulationsArrayHighWaterMark) {
+    gDiscardedData.mDiscardedAccumulations++;
+    return;
+  }
   if (gHistogramAccumulations->Length() == kHistogramAccumulationsArrayHighWaterMark) {
     DispatchIPCTimerFired();
   }
   gHistogramAccumulations->AppendElement(Accumulation{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<KeyedAccumulation>();
   }
+  if (gKeyedHistogramAccumulations->Length() >=
+      kWaterMarkDiscardFactor * kHistogramAccumulationsArrayHighWaterMark) {
+    gDiscardedData.mDiscardedKeyedAccumulations++;
+    return;
+  }
   if (gKeyedHistogramAccumulations->Length() == kHistogramAccumulationsArrayHighWaterMark) {
     DispatchIPCTimerFired();
   }
   gKeyedHistogramAccumulations->AppendElement(KeyedAccumulation{aId, aSample, aKey});
   ArmIPCTimer(locker);
 }
 
 void
 TelemetryIPCAccumulator::RecordChildScalarAction(mozilla::Telemetry::ScalarID aId,
                                                  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.
   gChildScalarsActions->AppendElement(ScalarAction{aId, aAction, Some(aValue)});
   ArmIPCTimer(locker);
 }
 
@@ -183,16 +205,21 @@ TelemetryIPCAccumulator::RecordChildKeye
                                                       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.
   gChildKeyedScalarsActions->AppendElement(
     KeyedScalarAction{aId, aAction, NS_ConvertUTF16toUTF8(aKey), Some(aValue)});
   ArmIPCTimer(locker);
 }
@@ -206,16 +233,22 @@ TelemetryIPCAccumulator::RecordChildEven
                                           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,
@@ -231,35 +264,37 @@ static void
 SendAccumulatedData(TActor* ipcActor)
 {
   // Get the accumulated data and free the storage buffers.
   nsTArray<Accumulation> accumulationsToSend;
   nsTArray<KeyedAccumulation> keyedAccumulationsToSend;
   nsTArray<ScalarAction> scalarsToSend;
   nsTArray<KeyedScalarAction> keyedScalarsToSend;
   nsTArray<ChildEventData> eventsToSend;
+  DiscardedData discardedData;
 
   {
     StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     if (gHistogramAccumulations) {
       accumulationsToSend.SwapElements(*gHistogramAccumulations);
     }
     if (gKeyedHistogramAccumulations) {
       keyedAccumulationsToSend.SwapElements(*gKeyedHistogramAccumulations);
     }
-    // Copy the scalar actions.
     if (gChildScalarsActions) {
       scalarsToSend.SwapElements(*gChildScalarsActions);
     }
     if (gChildKeyedScalarsActions) {
       keyedScalarsToSend.SwapElements(*gChildKeyedScalarsActions);
     }
     if (gChildEvents) {
       eventsToSend.SwapElements(*gChildEvents);
     }
+    discardedData = gDiscardedData;
+    gDiscardedData = {0};
   }
 
   // Send the accumulated data to the parent process.
   mozilla::Unused << NS_WARN_IF(!ipcActor);
   if (accumulationsToSend.Length()) {
     mozilla::Unused <<
       NS_WARN_IF(!ipcActor->SendAccumulateChildHistograms(accumulationsToSend));
   }
@@ -274,16 +309,18 @@ SendAccumulatedData(TActor* ipcActor)
   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