LCOV - code coverage report
Current view: top level - toolkit/components/telemetry/ipc - TelemetryIPCAccumulator.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 74 151 49.0 %
Date: 2017-07-14 16:53:18 Functions: 8 17 47.1 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
       2             : /* vim: set ts=8 sts=2 et sw=2 tw=80: */
       3             : /* This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #include "TelemetryIPCAccumulator.h"
       8             : 
       9             : #include "mozilla/dom/ContentChild.h"
      10             : #include "mozilla/gfx/GPUParent.h"
      11             : #include "mozilla/gfx/GPUProcessManager.h"
      12             : #include "mozilla/StaticMutex.h"
      13             : #include "mozilla/StaticPtr.h"
      14             : #include "mozilla/SystemGroup.h"
      15             : #include "mozilla/Unused.h"
      16             : #include "nsComponentManagerUtils.h"
      17             : #include "nsITimer.h"
      18             : #include "nsThreadUtils.h"
      19             : #include "TelemetryHistogram.h"
      20             : #include "TelemetryScalar.h"
      21             : 
      22             : using mozilla::StaticMutex;
      23             : using mozilla::StaticMutexAutoLock;
      24             : using mozilla::StaticAutoPtr;
      25             : using mozilla::SystemGroup;
      26             : using mozilla::TaskCategory;
      27             : using mozilla::Telemetry::Accumulation;
      28             : using mozilla::Telemetry::DiscardedData;
      29             : using mozilla::Telemetry::KeyedAccumulation;
      30             : using mozilla::Telemetry::ScalarActionType;
      31             : using mozilla::Telemetry::ScalarAction;
      32             : using mozilla::Telemetry::KeyedScalarAction;
      33             : using mozilla::Telemetry::ScalarVariant;
      34             : using mozilla::Telemetry::ChildEventData;
      35             : 
      36             : namespace TelemetryIPCAccumulator = mozilla::TelemetryIPCAccumulator;
      37             : 
      38             : // Sending each remote accumulation immediately places undue strain on the
      39             : // IPC subsystem. Batch the remote accumulations for a period of time before
      40             : // sending them all at once. This value was chosen as a balance between data
      41             : // timeliness and performance (see bug 1218576)
      42             : const uint32_t kBatchTimeoutMs = 2000;
      43             : 
      44             : // To stop growing unbounded in memory while waiting for kBatchTimeoutMs to
      45             : // drain the probe accumulation arrays, we request an immediate flush if the
      46             : // arrays manage to reach certain high water mark of elements.
      47             : const size_t kHistogramAccumulationsArrayHighWaterMark = 5 * 1024;
      48             : const size_t kScalarActionsArrayHighWaterMark = 10000;
      49             : // With the current limits, events cost us about 1100 bytes each.
      50             : // This limits memory use to about 10MB.
      51             : const size_t kEventsArrayHighWaterMark = 10000;
      52             : // If we are starved we can overshoot the watermark.
      53             : // This is the multiplier over which we will discard data.
      54             : const size_t kWaterMarkDiscardFactor = 5;
      55             : 
      56             : // Counts of how many pieces of data we have discarded.
      57             : DiscardedData gDiscardedData = {0};
      58             : 
      59             : // This timer is used for batching and sending child process accumulations to the parent.
      60             : nsITimer* gIPCTimer = nullptr;
      61             : mozilla::Atomic<bool, mozilla::Relaxed> gIPCTimerArmed(false);
      62             : mozilla::Atomic<bool, mozilla::Relaxed> gIPCTimerArming(false);
      63             : 
      64             : // This batches child process accumulations that should be sent to the parent.
      65           3 : StaticAutoPtr<nsTArray<Accumulation>> gHistogramAccumulations;
      66           3 : StaticAutoPtr<nsTArray<KeyedAccumulation>> gKeyedHistogramAccumulations;
      67           3 : StaticAutoPtr<nsTArray<ScalarAction>> gChildScalarsActions;
      68           3 : StaticAutoPtr<nsTArray<KeyedScalarAction>> gChildKeyedScalarsActions;
      69           3 : StaticAutoPtr<nsTArray<ChildEventData>> gChildEvents;
      70             : 
      71             : // This is a StaticMutex rather than a plain Mutex so that (1)
      72             : // it gets initialised in a thread-safe manner the first time
      73             : // it is used, and (2) because it is never de-initialised, and
      74             : // a normal Mutex would show up as a leak in BloatView.  StaticMutex
      75             : // also has the "OffTheBooks" property, so it won't show as a leak
      76             : // in BloatView.
      77           3 : static StaticMutex gTelemetryIPCAccumulatorMutex;
      78             : 
      79             : namespace {
      80             : 
      81             : void
      82           5 : DoArmIPCTimerMainThread(const StaticMutexAutoLock& lock)
      83             : {
      84           5 :   MOZ_ASSERT(NS_IsMainThread());
      85           5 :   gIPCTimerArming = false;
      86           5 :   if (gIPCTimerArmed) {
      87           0 :     return;
      88             :   }
      89           5 :   if (!gIPCTimer) {
      90           2 :     CallCreateInstance(NS_TIMER_CONTRACTID, &gIPCTimer);
      91           2 :     if (gIPCTimer) {
      92           2 :       gIPCTimer->SetTarget(SystemGroup::EventTargetFor(TaskCategory::Other));
      93             :     }
      94             :   }
      95           5 :   if (gIPCTimer) {
      96           5 :     gIPCTimer->InitWithNamedFuncCallback(TelemetryIPCAccumulator::IPCTimerFired,
      97             :                                          nullptr, kBatchTimeoutMs,
      98             :                                          nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY,
      99          10 :                                          "TelemetryIPCAccumulator::IPCTimerFired");
     100           5 :     gIPCTimerArmed = true;
     101             :   }
     102             : }
     103             : 
     104             : void
     105        1122 : ArmIPCTimer(const StaticMutexAutoLock& lock)
     106             : {
     107        1122 :   if (gIPCTimerArmed || gIPCTimerArming) {
     108        1117 :     return;
     109             :   }
     110           5 :   gIPCTimerArming = true;
     111           5 :   if (NS_IsMainThread()) {
     112           5 :     DoArmIPCTimerMainThread(lock);
     113             :   } else {
     114           0 :     TelemetryIPCAccumulator::DispatchToMainThread(NS_NewRunnableFunction(
     115             :                                                     "TelemetryIPCAccumulator::ArmIPCTimer",
     116           0 :                                                     []() -> void {
     117           0 :       StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     118           0 :       DoArmIPCTimerMainThread(locker);
     119           0 :     }));
     120             :   }
     121             : }
     122             : 
     123             : void
     124           0 : DispatchIPCTimerFired()
     125             : {
     126           0 :   TelemetryIPCAccumulator::DispatchToMainThread(
     127           0 :     NS_NewRunnableFunction("TelemetryIPCAccumulator::IPCTimerFired",
     128           0 :                            []() -> void {
     129           0 :       TelemetryIPCAccumulator::IPCTimerFired(nullptr, nullptr);
     130           0 :     }));
     131           0 : }
     132             : 
     133             : } // anonymous namespace
     134             : 
     135             : ////////////////////////////////////////////////////////////////////////
     136             : ////////////////////////////////////////////////////////////////////////
     137             : //
     138             : // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryIPCAccumulator::
     139             : 
     140             : void
     141         828 : TelemetryIPCAccumulator::AccumulateChildHistogram(mozilla::Telemetry::HistogramID aId,
     142             :                                                   uint32_t aSample)
     143             : {
     144        1656 :   StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     145         828 :   if (!gHistogramAccumulations) {
     146           2 :     gHistogramAccumulations = new nsTArray<Accumulation>();
     147             :   }
     148         828 :   if (gHistogramAccumulations->Length() >=
     149             :       kWaterMarkDiscardFactor * kHistogramAccumulationsArrayHighWaterMark) {
     150           0 :     gDiscardedData.mDiscardedHistogramAccumulations++;
     151           0 :     return;
     152             :   }
     153         828 :   if (gHistogramAccumulations->Length() == kHistogramAccumulationsArrayHighWaterMark) {
     154           0 :     DispatchIPCTimerFired();
     155             :   }
     156         828 :   gHistogramAccumulations->AppendElement(Accumulation{aId, aSample});
     157         828 :   ArmIPCTimer(locker);
     158             : }
     159             : 
     160             : void
     161         294 : TelemetryIPCAccumulator::AccumulateChildKeyedHistogram(mozilla::Telemetry::HistogramID aId,
     162             :                                                        const nsCString& aKey, uint32_t aSample)
     163             : {
     164         588 :   StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     165         294 :   if (!gKeyedHistogramAccumulations) {
     166           2 :     gKeyedHistogramAccumulations = new nsTArray<KeyedAccumulation>();
     167             :   }
     168         294 :   if (gKeyedHistogramAccumulations->Length() >=
     169             :       kWaterMarkDiscardFactor * kHistogramAccumulationsArrayHighWaterMark) {
     170           0 :     gDiscardedData.mDiscardedKeyedHistogramAccumulations++;
     171           0 :     return;
     172             :   }
     173         294 :   if (gKeyedHistogramAccumulations->Length() == kHistogramAccumulationsArrayHighWaterMark) {
     174           0 :     DispatchIPCTimerFired();
     175             :   }
     176         294 :   gKeyedHistogramAccumulations->AppendElement(KeyedAccumulation{aId, aSample, aKey});
     177         294 :   ArmIPCTimer(locker);
     178             : }
     179             : 
     180             : void
     181           0 : TelemetryIPCAccumulator::RecordChildScalarAction(mozilla::Telemetry::ScalarID aId,
     182             :                                                  ScalarActionType aAction, const ScalarVariant& aValue)
     183             : {
     184           0 :   StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     185             :   // Make sure to have the storage.
     186           0 :   if (!gChildScalarsActions) {
     187           0 :     gChildScalarsActions = new nsTArray<ScalarAction>();
     188             :   }
     189           0 :   if (gChildScalarsActions->Length() >=
     190             :       kWaterMarkDiscardFactor * kScalarActionsArrayHighWaterMark) {
     191           0 :     gDiscardedData.mDiscardedScalarActions++;
     192           0 :     return;
     193             :   }
     194           0 :   if (gChildScalarsActions->Length() == kScalarActionsArrayHighWaterMark) {
     195           0 :     DispatchIPCTimerFired();
     196             :   }
     197             :   // Store the action.
     198           0 :   gChildScalarsActions->AppendElement(ScalarAction{aId, aAction, Some(aValue)});
     199           0 :   ArmIPCTimer(locker);
     200             : }
     201             : 
     202             : void
     203           0 : TelemetryIPCAccumulator::RecordChildKeyedScalarAction(mozilla::Telemetry::ScalarID aId,
     204             :                                                       const nsAString& aKey,
     205             :                                                       ScalarActionType aAction,
     206             :                                                       const ScalarVariant& aValue)
     207             : {
     208           0 :   StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     209             :   // Make sure to have the storage.
     210           0 :   if (!gChildKeyedScalarsActions) {
     211           0 :     gChildKeyedScalarsActions = new nsTArray<KeyedScalarAction>();
     212             :   }
     213           0 :   if (gChildKeyedScalarsActions->Length() >=
     214             :       kWaterMarkDiscardFactor * kScalarActionsArrayHighWaterMark) {
     215           0 :     gDiscardedData.mDiscardedKeyedScalarActions++;
     216           0 :     return;
     217             :   }
     218           0 :   if (gChildKeyedScalarsActions->Length() == kScalarActionsArrayHighWaterMark) {
     219           0 :     DispatchIPCTimerFired();
     220             :   }
     221             :   // Store the action.
     222           0 :   gChildKeyedScalarsActions->AppendElement(
     223           0 :     KeyedScalarAction{aId, aAction, NS_ConvertUTF16toUTF8(aKey), Some(aValue)});
     224           0 :   ArmIPCTimer(locker);
     225             : }
     226             : 
     227             : void
     228           0 : TelemetryIPCAccumulator::RecordChildEvent(const mozilla::TimeStamp& timestamp,
     229             :                                           const nsACString& category,
     230             :                                           const nsACString& method,
     231             :                                           const nsACString& object,
     232             :                                           const mozilla::Maybe<nsCString>& value,
     233             :                                           const nsTArray<mozilla::Telemetry::EventExtraEntry>& extra)
     234             : {
     235           0 :   StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     236             : 
     237           0 :   if (!gChildEvents) {
     238           0 :     gChildEvents = new nsTArray<ChildEventData>();
     239             :   }
     240             : 
     241           0 :   if (gChildEvents->Length() >=
     242             :       kWaterMarkDiscardFactor * kEventsArrayHighWaterMark) {
     243           0 :     gDiscardedData.mDiscardedChildEvents++;
     244           0 :     return;
     245             :   }
     246             : 
     247           0 :   if (gChildEvents->Length() == kEventsArrayHighWaterMark) {
     248           0 :     DispatchIPCTimerFired();
     249             :   }
     250             : 
     251             :   // Store the event.
     252           0 :   gChildEvents->AppendElement(ChildEventData{timestamp, nsCString(category),
     253             :                                              nsCString(method), nsCString(object),
     254             :                                              value,
     255           0 :                                              nsTArray<mozilla::Telemetry::EventExtraEntry>(extra)});
     256           0 :   ArmIPCTimer(locker);
     257             : }
     258             : 
     259             : // This method takes the lock only to double-buffer the batched telemetry.
     260             : // It releases the lock before calling out to IPC code which can (and does)
     261             : // Accumulate (which would deadlock)
     262             : template<class TActor>
     263             : static void
     264           3 : SendAccumulatedData(TActor* ipcActor)
     265             : {
     266             :   // Get the accumulated data and free the storage buffers.
     267           6 :   nsTArray<Accumulation> accumulationsToSend;
     268           6 :   nsTArray<KeyedAccumulation> keyedAccumulationsToSend;
     269           6 :   nsTArray<ScalarAction> scalarsToSend;
     270           6 :   nsTArray<KeyedScalarAction> keyedScalarsToSend;
     271           6 :   nsTArray<ChildEventData> eventsToSend;
     272             :   DiscardedData discardedData;
     273             : 
     274             :   {
     275           6 :     StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     276           3 :     if (gHistogramAccumulations) {
     277           3 :       accumulationsToSend.SwapElements(*gHistogramAccumulations);
     278             :     }
     279           3 :     if (gKeyedHistogramAccumulations) {
     280           3 :       keyedAccumulationsToSend.SwapElements(*gKeyedHistogramAccumulations);
     281             :     }
     282           3 :     if (gChildScalarsActions) {
     283           0 :       scalarsToSend.SwapElements(*gChildScalarsActions);
     284             :     }
     285           3 :     if (gChildKeyedScalarsActions) {
     286           0 :       keyedScalarsToSend.SwapElements(*gChildKeyedScalarsActions);
     287             :     }
     288           3 :     if (gChildEvents) {
     289           0 :       eventsToSend.SwapElements(*gChildEvents);
     290             :     }
     291           3 :     discardedData = gDiscardedData;
     292           3 :     gDiscardedData = {0};
     293             :   }
     294             : 
     295             :   // Send the accumulated data to the parent process.
     296           3 :   mozilla::Unused << NS_WARN_IF(!ipcActor);
     297           3 :   if (accumulationsToSend.Length()) {
     298             :     mozilla::Unused <<
     299           3 :       NS_WARN_IF(!ipcActor->SendAccumulateChildHistograms(accumulationsToSend));
     300             :   }
     301           3 :   if (keyedAccumulationsToSend.Length()) {
     302             :     mozilla::Unused <<
     303           3 :       NS_WARN_IF(!ipcActor->SendAccumulateChildKeyedHistograms(keyedAccumulationsToSend));
     304             :   }
     305           3 :   if (scalarsToSend.Length()) {
     306             :     mozilla::Unused <<
     307           0 :       NS_WARN_IF(!ipcActor->SendUpdateChildScalars(scalarsToSend));
     308             :   }
     309           3 :   if (keyedScalarsToSend.Length()) {
     310             :     mozilla::Unused <<
     311           0 :       NS_WARN_IF(!ipcActor->SendUpdateChildKeyedScalars(keyedScalarsToSend));
     312             :   }
     313           3 :   if (eventsToSend.Length()) {
     314             :     mozilla::Unused <<
     315           0 :       NS_WARN_IF(!ipcActor->SendRecordChildEvents(eventsToSend));
     316             :   }
     317             :   mozilla::Unused <<
     318           3 :     NS_WARN_IF(!ipcActor->SendRecordDiscardedData(discardedData));
     319           3 : }
     320             : 
     321             : 
     322             : // To ensure we don't loop IPCTimerFired->AccumulateChild->arm timer, we don't
     323             : // unset gIPCTimerArmed until the IPC completes
     324             : //
     325             : // This function must be called on the main thread, otherwise IPC will fail.
     326             : void
     327           3 : TelemetryIPCAccumulator::IPCTimerFired(nsITimer* aTimer, void* aClosure)
     328             : {
     329           3 :   MOZ_ASSERT(NS_IsMainThread());
     330             : 
     331             :   // Send accumulated data to the correct parent process.
     332           3 :   switch (XRE_GetProcessType()) {
     333             :     case GeckoProcessType_Content:
     334           3 :       SendAccumulatedData(mozilla::dom::ContentChild::GetSingleton());
     335           3 :       break;
     336             :     case GeckoProcessType_GPU:
     337           0 :       SendAccumulatedData(mozilla::gfx::GPUParent::GetSingleton());
     338           0 :       break;
     339             :     default:
     340           0 :       MOZ_ASSERT_UNREACHABLE("Unsupported process type");
     341             :       break;
     342             :   }
     343             : 
     344           3 :   gIPCTimerArmed = false;
     345           3 : }
     346             : 
     347             : void
     348           0 : TelemetryIPCAccumulator::DeInitializeGlobalState()
     349             : {
     350           0 :   MOZ_ASSERT(NS_IsMainThread());
     351             : 
     352           0 :   StaticMutexAutoLock locker(gTelemetryIPCAccumulatorMutex);
     353           0 :   if (gIPCTimer) {
     354           0 :     NS_RELEASE(gIPCTimer);
     355             :   }
     356             : 
     357           0 :   gHistogramAccumulations = nullptr;
     358           0 :   gKeyedHistogramAccumulations = nullptr;
     359           0 :   gChildScalarsActions = nullptr;
     360           0 :   gChildKeyedScalarsActions = nullptr;
     361           0 :   gChildEvents = nullptr;
     362           0 : }
     363             : 
     364             : void
     365           0 : TelemetryIPCAccumulator::DispatchToMainThread(already_AddRefed<nsIRunnable>&& aEvent)
     366             : {
     367           0 :   SystemGroup::EventTargetFor(TaskCategory::Other)->Dispatch(Move(aEvent),
     368           0 :                                                              nsIEventTarget::DISPATCH_NORMAL);
     369           9 : }

Generated by: LCOV version 1.13