From 7876f2ef1e6bb582e8cc5aa21d96aa4b4923841e Mon Sep 17 00:00:00 2001 From: Chris H-C Date: Wed, 22 Jun 2016 10:16:40 -0400 Subject: [PATCH] bug 1218576 - Accumulate child histograms in the parent process r=froydnj Batch the accumulations to only transmit every so often, so we don't incur too much in the way of IPC overhead penalties. What this doesn't do: * remove or restructure child telemetry code to adapt to the new way * send the telemetry anywhere * allow for the child process to clear child histograms * support anything but histograms (but this is expected and okay) MozReview-Commit-ID: JnUkcmN3Ya7 --- dom/ipc/ContentParent.cpp | 16 ++ dom/ipc/ContentParent.h | 4 + dom/ipc/PContent.ipdl | 8 + toolkit/components/telemetry/Telemetry.cpp | 12 + toolkit/components/telemetry/Telemetry.h | 17 ++ toolkit/components/telemetry/TelemetryComms.h | 84 +++++++ .../telemetry/TelemetryHistogram.cpp | 208 ++++++++++++++++-- .../components/telemetry/TelemetryHistogram.h | 7 + toolkit/components/telemetry/moz.build | 1 + 9 files changed, 344 insertions(+), 13 deletions(-) create mode 100644 toolkit/components/telemetry/TelemetryComms.h diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index a2dda3d2ad80..495efb0d79ed 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5413,3 +5413,19 @@ ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch } ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch); } + +bool +ContentParent::RecvAccumulateChildHistogram( + InfallibleTArray&& aAccumulations) +{ + Telemetry::AccumulateChild(aAccumulations); + return true; +} + +bool +ContentParent::RecvAccumulateChildKeyedHistogram( + InfallibleTArray&& aAccumulations) +{ + Telemetry::AccumulateChildKeyed(aAccumulations); + return true; +} diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 4d992d499eef..e16f2c842b04 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -1137,6 +1137,10 @@ private: virtual bool RecvDeleteGetFilesRequest(const nsID& aID) override; + virtual bool RecvAccumulateChildHistogram( + InfallibleTArray&& aAccumulations) override; + virtual bool RecvAccumulateChildKeyedHistogram( + InfallibleTArray&& aAccumulations) override; public: void SendGetFilesResponseAndForget(const nsID& aID, const GetFilesResponseResult& aResult); diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index b47af2b642cf..03aa98a39f01 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -100,6 +100,8 @@ using mozilla::DataStorageType from "ipc/DataStorageIPCUtils.h"; using mozilla::DocShellOriginAttributes from "mozilla/ipc/BackgroundUtils.h"; using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.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"; union ChromeRegistryItem { @@ -1216,6 +1218,12 @@ parent: async UnstoreAndBroadcastBlobURLUnregistration(nsCString url); + /** + * Messages for communicating child Telemetry to the parent process + */ + async AccumulateChildHistogram(Accumulation[] accumulations); + async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations); + both: async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, Principal aPrincipal, ClonedMessageData aData); diff --git a/toolkit/components/telemetry/Telemetry.cpp b/toolkit/components/telemetry/Telemetry.cpp index 931574f6135d..d04e255e23ac 100644 --- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -2793,6 +2793,18 @@ AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end) static_cast((end - start).ToMilliseconds())); } +void +AccumulateChild(const nsTArray& aAccumulations) +{ + TelemetryHistogram::AccumulateChild(aAccumulations); +} + +void +AccumulateChildKeyed(const nsTArray& aAccumulations) +{ + TelemetryHistogram::AccumulateChildKeyed(aAccumulations); +} + void ClearHistogram(ID aId) { diff --git a/toolkit/components/telemetry/Telemetry.h b/toolkit/components/telemetry/Telemetry.h index 6084c209898e..0204036f4b03 100644 --- a/toolkit/components/telemetry/Telemetry.h +++ b/toolkit/components/telemetry/Telemetry.h @@ -33,6 +33,9 @@ namespace HangMonitor { } // namespace HangMonitor namespace Telemetry { +struct Accumulation; +struct KeyedAccumulation; + enum TimerResolution { Millisecond, Microsecond @@ -125,6 +128,20 @@ void AccumulateCategorical(ID id, const nsCString& label); */ void AccumulateTimeDelta(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now()); +/** + * Accumulate child data into child histograms + * + * @param aAccumulations - accumulation actions to perform + */ +void AccumulateChild(const nsTArray& aAccumulations); + +/** + * Accumulate child data into child keyed histograms + * + * @param aAccumulations - accumulation actions to perform + */ +void AccumulateChildKeyed(const nsTArray& aAccumulations); + /** * This clears the data for a histogram in TelemetryHistogramEnums.h. * diff --git a/toolkit/components/telemetry/TelemetryComms.h b/toolkit/components/telemetry/TelemetryComms.h new file mode 100644 index 000000000000..0f2d888e3198 --- /dev/null +++ b/toolkit/components/telemetry/TelemetryComms.h @@ -0,0 +1,84 @@ +/* -*- 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 + */ + +#ifndef Telemetry_Comms_h__ +#define Telemetry_Comms_h__ + +#include "ipc/IPCMessageUtils.h" + +namespace mozilla { +namespace Telemetry { + +enum ID : uint32_t; + +struct Accumulation +{ + mozilla::Telemetry::ID mId; + uint32_t mSample; +}; + +struct KeyedAccumulation +{ + mozilla::Telemetry::ID mId; + uint32_t mSample; + nsCString mKey; +}; + +} // namespace Telemetry +} // namespace mozilla + +namespace IPC { + +template<> +struct +ParamTraits +{ + typedef mozilla::Telemetry::Accumulation paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + aMsg->WriteUInt32(aParam.mId); + WriteParam(aMsg, aParam.mSample); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + if (!aMsg->ReadUInt32(aIter, reinterpret_cast(&(aResult->mId))) || + !ReadParam(aMsg, aIter, &(aResult->mSample))) { + return false; + } + + return true; + } +}; + +template<> +struct +ParamTraits +{ + typedef mozilla::Telemetry::KeyedAccumulation paramType; + + static void Write(Message* aMsg, const paramType& aParam) + { + aMsg->WriteUInt32(aParam.mId); + WriteParam(aMsg, aParam.mSample); + WriteParam(aMsg, aParam.mKey); + } + + static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) + { + if (!aMsg->ReadUInt32(aIter, reinterpret_cast(&(aResult->mId))) || + !ReadParam(aMsg, aIter, &(aResult->mSample)) || + !ReadParam(aMsg, aIter, &(aResult->mKey))) { + return false; + } + + return true; + } +}; + +} // namespace IPC + +#endif // Telemetry_Comms_h__ diff --git a/toolkit/components/telemetry/TelemetryHistogram.cpp b/toolkit/components/telemetry/TelemetryHistogram.cpp index 1aae6bb539f7..6c25a6145743 100644 --- a/toolkit/components/telemetry/TelemetryHistogram.cpp +++ b/toolkit/components/telemetry/TelemetryHistogram.cpp @@ -14,9 +14,12 @@ #include "nsClassHashtable.h" #include "nsITelemetry.h" +#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/StartupTimeline.h" #include "mozilla/StaticMutex.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/unused.h" #include "TelemetryCommon.h" #include "TelemetryHistogram.h" @@ -31,6 +34,9 @@ using base::FlagHistogram; using base::LinearHistogram; using mozilla::StaticMutex; using mozilla::StaticMutexAutoLock; +using mozilla::StaticAutoPtr; +using mozilla::Telemetry::Accumulation; +using mozilla::Telemetry::KeyedAccumulation; //////////////////////////////////////////////////////////////////////// @@ -92,6 +98,7 @@ using mozilla::StaticMutexAutoLock; #define EXPIRED_ID "__expired__" #define SUBSESSION_HISTOGRAM_PREFIX "sub#" #define KEYED_HISTOGRAM_NAME_SEPARATOR "#" +#define CHILD_HISTOGRAM_SUFFIX "#content" namespace { @@ -185,6 +192,12 @@ AddonMapType gAddonMap; // The singleton StatisticsRecorder object for this process. base::StatisticsRecorder* gStatisticsRecorder = nullptr; +// For batching and sending child process accumulations to the parent +nsITimer* gIPCTimer = nullptr; +bool gIPCTimerArmed = false; +StaticAutoPtr> gAccumulations; +StaticAutoPtr> gKeyedAccumulations; + } // namespace @@ -204,6 +217,12 @@ const mozilla::Telemetry::ID kRecordingInitiallyDisabledIDs[] = { mozilla::Telemetry::TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD }; +// Sending each remote accumulation immediately places undue strain on the +// IPC subsystem. Batch the remote accumulations for a period of time before +// sending them all at once. This value was chosen as a balance between data +// timeliness and performance (see bug 1218576) +const uint32_t kBatchTimeoutMs = 2000; + } // namespace @@ -412,10 +431,12 @@ internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::ID *id) // O(1) histogram lookup by numeric id nsresult -internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret) +internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret, + bool child = false) { static Histogram* knownHistograms[mozilla::Telemetry::HistogramCount] = {0}; - Histogram *h = knownHistograms[id]; + static Histogram* knownChildHistograms[mozilla::Telemetry::HistogramCount] = {0}; + Histogram *h = child ? knownChildHistograms[id] : knownHistograms[id]; if (h) { *ret = h; return NS_OK; @@ -426,8 +447,15 @@ internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret) return NS_ERROR_FAILURE; } - nsresult rv = internal_HistogramGet(p.id(), p.expiration(), p.histogramType, - p.min, p.max, p.bucketCount, true, &h); + nsCString histogramName; + histogramName.Append(p.id()); + if (child) { + histogramName.AppendLiteral(CHILD_HISTOGRAM_SUFFIX); + } + + nsresult rv = internal_HistogramGet(histogramName.get(), p.expiration(), + p.histogramType, p.min, p.max, + p.bucketCount, true, &h); if (NS_FAILED(rv)) return rv; @@ -447,7 +475,11 @@ internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret) } #endif - *ret = knownHistograms[id] = h; + if (child) { + *ret = knownChildHistograms[id] = h; + } else { + *ret = knownHistograms[id] = h; + } return NS_OK; } @@ -1681,9 +1713,55 @@ internal_SetHistogramRecordingEnabled(mozilla::Telemetry::ID aID, bool aEnabled) MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found"); } +void internal_armIPCTimer() +{ + if (gIPCTimerArmed) { + return; + } + if (!gIPCTimer) { + CallCreateInstance(NS_TIMER_CONTRACTID, &gIPCTimer); + } + if (gIPCTimer) { + gIPCTimer->InitWithFuncCallback(TelemetryHistogram::IPCTimerFired, + nullptr, kBatchTimeoutMs, + nsITimer::TYPE_ONE_SHOT); + gIPCTimerArmed = true; + } +} + +bool +internal_RemoteAccumulate(mozilla::Telemetry::ID aId, uint32_t aSample) +{ + if (XRE_IsParentProcess()) { + return false; + } + if (!gAccumulations) { + gAccumulations = new nsTArray(); + } + gAccumulations->AppendElement(Accumulation{aId, aSample}); + internal_armIPCTimer(); + return true; +} + +bool +internal_RemoteAccumulate(mozilla::Telemetry::ID aId, + const nsCString& aKey, uint32_t aSample) +{ + if (XRE_IsParentProcess()) { + return false; + } + if (!gKeyedAccumulations) { + gKeyedAccumulations = new nsTArray(); + } + gKeyedAccumulations->AppendElement(KeyedAccumulation{aId, aSample, aKey}); + internal_armIPCTimer(); + return true; +} + void internal_Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample) { - if (!internal_CanRecordBase()) { + if (!internal_CanRecordBase() || + internal_RemoteAccumulate(aHistogram, aSample)) { return; } Histogram *h; @@ -1697,7 +1775,8 @@ void internal_Accumulate(mozilla::Telemetry::ID aID, const nsCString& aKey, uint32_t aSample) { - if (!gInitDone || !internal_CanRecordBase()) { + if (!gInitDone || !internal_CanRecordBase() || + internal_RemoteAccumulate(aID, aKey, aSample)) { return; } const HistogramInfo& th = gHistograms[aID]; @@ -1707,6 +1786,42 @@ internal_Accumulate(mozilla::Telemetry::ID aID, keyed->Add(aKey, aSample); } +void +internal_AccumulateChild(mozilla::Telemetry::ID aId, uint32_t aSample) +{ + if (!internal_CanRecordBase()) { + return; + } + Histogram* h; + nsresult rv = internal_GetHistogramByEnumId(aId, &h, true); + if (NS_SUCCEEDED(rv)) { + internal_HistogramAdd(*h, aSample, gHistograms[aId].dataset); + } else { + NS_WARNING("NS_FAILED GetHistogramByEnumId for CHILD"); + } +} + +void +internal_AccumulateChildKeyed(mozilla::Telemetry::ID aId, + const nsCString& aKey, uint32_t aSample) +{ + if (!gInitDone || !internal_CanRecordBase()) { + return; + } + const HistogramInfo& th = gHistograms[aId]; + nsCString id; + id.Append(th.id()); + id.AppendLiteral(CHILD_HISTOGRAM_SUFFIX); + KeyedHistogram* keyed = internal_GetKeyedHistogramById(id); + if (!keyed) { + const nsDependentCString expiration(th.expiration()); + keyed = new KeyedHistogram(id, expiration, th.histogramType, th.min, th.max, + th.bucketCount, th.dataset); + gKeyedHistograms.Put(id, keyed); + } + keyed->Add(aKey, aSample); +} + } // namespace @@ -1816,6 +1931,11 @@ void TelemetryHistogram::DeInitializeGlobalState() gHistogramMap.Clear(); gKeyedHistograms.Clear(); gAddonMap.Clear(); + gAccumulations = nullptr; + gKeyedAccumulations = nullptr; + if (gIPCTimer) { + NS_RELEASE(gIPCTimer); + } gInitDone = false; } @@ -1921,12 +2041,7 @@ TelemetryHistogram::Accumulate(const char* name, uint32_t sample) if (NS_FAILED(rv)) { return; } - - Histogram *h; - rv = internal_GetHistogramByEnumId(id, &h); - if (NS_SUCCEEDED(rv)) { - internal_HistogramAdd(*h, sample, gHistograms[id].dataset); - } + internal_Accumulate(id, sample); } void @@ -1952,6 +2067,34 @@ TelemetryHistogram::AccumulateCategorical(mozilla::Telemetry::ID aId, internal_HistogramAddCategorical(aId, label); } +void +TelemetryHistogram::AccumulateChild(const nsTArray& aAccumulations) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + StaticMutexAutoLock locker(gTelemetryHistogramMutex); + if (!internal_CanRecordBase()) { + return; + } + for (uint32_t i = 0; i < aAccumulations.Length(); ++i) { + internal_AccumulateChild(aAccumulations[i].mId, aAccumulations[i].mSample); + } +} + +void +TelemetryHistogram::AccumulateChildKeyed(const nsTArray& aAccumulations) +{ + MOZ_ASSERT(XRE_IsParentProcess()); + StaticMutexAutoLock locker(gTelemetryHistogramMutex); + if (!internal_CanRecordBase()) { + return; + } + for (uint32_t i = 0; i < aAccumulations.Length(); ++i) { + internal_AccumulateChildKeyed(aAccumulations[i].mId, + aAccumulations[i].mKey, + aAccumulations[i].mSample); + } +} + void TelemetryHistogram::ClearHistogram(mozilla::Telemetry::ID aId) { @@ -2058,6 +2201,8 @@ TelemetryHistogram::CreateHistogramSnapshots(JSContext *cx, mozilla::DebugOnly rv = internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h); MOZ_ASSERT(NS_SUCCEEDED(rv)); + rv = internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h, true); + MOZ_ASSERT(NS_SUCCEEDED(rv)); } } @@ -2321,3 +2466,40 @@ TelemetryHistogram::GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf } return n; } + +// 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) +// +// To ensure non-reentrancy, the timer is not released until the method +// completes +void +TelemetryHistogram::IPCTimerFired(nsITimer* aTimer, void* aClosure) +{ + nsTArray accumulationsToSend; + nsTArray keyedAccumulationsToSend; + { + StaticMutexAutoLock locker(gTelemetryHistogramMutex); + if (gAccumulations) { + accumulationsToSend.SwapElements(*gAccumulations); + } + if (gKeyedAccumulations) { + keyedAccumulationsToSend.SwapElements(*gKeyedAccumulations); + } + } + + mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton(); + mozilla::Unused << NS_WARN_IF(!contentChild); + if (contentChild) { + if (accumulationsToSend.Length()) { + mozilla::Unused << + NS_WARN_IF(!contentChild->SendAccumulateChildHistogram(accumulationsToSend)); + } + if (keyedAccumulationsToSend.Length()) { + mozilla::Unused << + NS_WARN_IF(!contentChild->SendAccumulateChildKeyedHistogram(keyedAccumulationsToSend)); + } + } + + gIPCTimerArmed = false; +} diff --git a/toolkit/components/telemetry/TelemetryHistogram.h b/toolkit/components/telemetry/TelemetryHistogram.h index 2995ae4c0208..d3a684da3cde 100644 --- a/toolkit/components/telemetry/TelemetryHistogram.h +++ b/toolkit/components/telemetry/TelemetryHistogram.h @@ -8,6 +8,8 @@ #include "mozilla/TelemetryHistogramEnums.h" +#include "mozilla/TelemetryComms.h" + // This module is internal to Telemetry. It encapsulates Telemetry's // histogram accumulation and storage logic. It should only be used by // Telemetry.cpp. These functions should not be used anywhere else. @@ -42,6 +44,9 @@ void Accumulate(const char* name, const nsCString& key, uint32_t sample); void AccumulateCategorical(mozilla::Telemetry::ID aId, const nsCString& aLabel); +void AccumulateChild(const nsTArray& aAccumulations); +void AccumulateChildKeyed(const nsTArray& aAccumulations); + void ClearHistogram(mozilla::Telemetry::ID aId); @@ -96,6 +101,8 @@ GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf); size_t GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); +void +IPCTimerFired(nsITimer* aTimer, void* aClosure); } // namespace TelemetryHistogram #endif // TelemetryHistogram_h__ diff --git a/toolkit/components/telemetry/moz.build b/toolkit/components/telemetry/moz.build index b2cff123823a..7e38babc2d44 100644 --- a/toolkit/components/telemetry/moz.build +++ b/toolkit/components/telemetry/moz.build @@ -19,6 +19,7 @@ EXPORTS.mozilla += [ '!TelemetryScalarEnums.h', 'ProcessedStack.h', 'Telemetry.h', + 'TelemetryComms.h', 'ThreadHangStats.h', ]