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
This commit is contained in:
Chris H-C 2016-06-22 10:16:40 -04:00
parent c0e2f75813
commit 7876f2ef1e
9 changed files with 344 additions and 13 deletions

View File

@ -5413,3 +5413,19 @@ ContentParent::ForceTabPaint(TabParent* aTabParent, uint64_t aLayerObserverEpoch
}
ProcessHangMonitor::ForcePaint(mHangMonitorActor, aTabParent, aLayerObserverEpoch);
}
bool
ContentParent::RecvAccumulateChildHistogram(
InfallibleTArray<Accumulation>&& aAccumulations)
{
Telemetry::AccumulateChild(aAccumulations);
return true;
}
bool
ContentParent::RecvAccumulateChildKeyedHistogram(
InfallibleTArray<KeyedAccumulation>&& aAccumulations)
{
Telemetry::AccumulateChildKeyed(aAccumulations);
return true;
}

View File

@ -1137,6 +1137,10 @@ private:
virtual bool RecvDeleteGetFilesRequest(const nsID& aID) override;
virtual bool RecvAccumulateChildHistogram(
InfallibleTArray<Accumulation>&& aAccumulations) override;
virtual bool RecvAccumulateChildKeyedHistogram(
InfallibleTArray<KeyedAccumulation>&& aAccumulations) override;
public:
void SendGetFilesResponseAndForget(const nsID& aID,
const GetFilesResponseResult& aResult);

View File

@ -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);

View File

@ -2793,6 +2793,18 @@ AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end)
static_cast<uint32_t>((end - start).ToMilliseconds()));
}
void
AccumulateChild(const nsTArray<Accumulation>& aAccumulations)
{
TelemetryHistogram::AccumulateChild(aAccumulations);
}
void
AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations)
{
TelemetryHistogram::AccumulateChildKeyed(aAccumulations);
}
void
ClearHistogram(ID aId)
{

View File

@ -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<Accumulation>& aAccumulations);
/**
* Accumulate child data into child keyed histograms
*
* @param aAccumulations - accumulation actions to perform
*/
void AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations);
/**
* This clears the data for a histogram in TelemetryHistogramEnums.h.
*

View File

@ -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<mozilla::Telemetry::Accumulation>
{
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<uint32_t*>(&(aResult->mId))) ||
!ReadParam(aMsg, aIter, &(aResult->mSample))) {
return false;
}
return true;
}
};
template<>
struct
ParamTraits<mozilla::Telemetry::KeyedAccumulation>
{
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<uint32_t*>(&(aResult->mId))) ||
!ReadParam(aMsg, aIter, &(aResult->mSample)) ||
!ReadParam(aMsg, aIter, &(aResult->mKey))) {
return false;
}
return true;
}
};
} // namespace IPC
#endif // Telemetry_Comms_h__

View File

@ -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<nsTArray<Accumulation>> gAccumulations;
StaticAutoPtr<nsTArray<KeyedAccumulation>> 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<Accumulation>();
}
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<KeyedAccumulation>();
}
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<Accumulation>& 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<KeyedAccumulation>& 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<nsresult> 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<Accumulation> accumulationsToSend;
nsTArray<KeyedAccumulation> 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;
}

View File

@ -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<mozilla::Telemetry::Accumulation>& aAccumulations);
void AccumulateChildKeyed(const nsTArray<mozilla::Telemetry::KeyedAccumulation>& 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__

View File

@ -19,6 +19,7 @@ EXPORTS.mozilla += [
'!TelemetryScalarEnums.h',
'ProcessedStack.h',
'Telemetry.h',
'TelemetryComms.h',
'ThreadHangStats.h',
]