Bug 1188376: Split Hello Telemetry values from general WebRTC r=jib
authorRandell Jesup <rjesup@jesup.org>
Tue, 11 Aug 2015 15:15:06 -0400
changeset 289965 5d44c5c1195c8f8880f3d76e03576f35dc1b78d6
parent 289964 0a170e64625c2d06c62b829352f0eece1455a0a5
child 289966 bd0b693e8c8f9f15b5e9af08980a12434a9d43b0
push id5245
push userraliiev@mozilla.com
push dateThu, 29 Oct 2015 11:30:51 +0000
treeherdermozilla-beta@dac831dc1bd0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjib
bugs1188376
milestone43.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1188376: Split Hello Telemetry values from general WebRTC r=jib
media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
toolkit/components/telemetry/Histograms.json
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionCtx.cpp
@@ -200,16 +200,17 @@ EverySecondTelemetryCallback_s(nsAutoPtr
   if(!PeerConnectionCtx::isActive()) {
     return;
   }
   PeerConnectionCtx *ctx = PeerConnectionCtx::GetInstance();
 
   for (auto q = aQueryList->begin(); q != aQueryList->end(); ++q) {
     PeerConnectionImpl::ExecuteStatsQuery_s(*q);
     auto& r = *(*q)->report;
+    bool isHello = (*q)->isHello;
     if (r.mInboundRTPStreamStats.WasPassed()) {
       // First, get reports from a second ago, if any, for calculations below
       const Sequence<RTCInboundRTPStreamStats> *lastInboundStats = nullptr;
       {
         auto i = FindId(ctx->mLastReports, r.mPcid);
         if (i != ctx->mLastReports.NoIndex) {
           lastInboundStats = &ctx->mLastReports[i]->mInboundRTPStreamStats.Value();
         }
@@ -232,37 +233,60 @@ EverySecondTelemetryCallback_s(nsAutoPtr
                      (isAudio? WEBRTC_AUDIO_QUALITY_OUTBOUND_JITTER :
                                WEBRTC_VIDEO_QUALITY_OUTBOUND_JITTER) :
                      (isAudio? WEBRTC_AUDIO_QUALITY_INBOUND_JITTER :
                                WEBRTC_VIDEO_QUALITY_INBOUND_JITTER),
                       s.mJitter.Value());
         }
         if (s.mMozRtt.WasPassed()) {
           MOZ_ASSERT(s.mIsRemote);
-          Accumulate(isAudio? WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT :
-                              WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT,
-                      s.mMozRtt.Value());
+          ID id;
+          if (isAudio) {
+            id = isHello ? LOOP_AUDIO_QUALITY_OUTBOUND_RTT :
+                           WEBRTC_AUDIO_QUALITY_OUTBOUND_RTT;
+          } else {
+            id = isHello ? LOOP_VIDEO_QUALITY_OUTBOUND_RTT :
+                           WEBRTC_VIDEO_QUALITY_OUTBOUND_RTT;
+          }
+          Accumulate(id, s.mMozRtt.Value());
         }
         if (lastInboundStats && s.mBytesReceived.WasPassed()) {
           auto& laststats = *lastInboundStats;
           auto i = FindId(laststats, s.mId.Value());
           if (i != laststats.NoIndex) {
             auto& lasts = laststats[i];
             if (lasts.mBytesReceived.WasPassed()) {
               auto delta_ms = int32_t(s.mTimestamp.Value() -
                                       lasts.mTimestamp.Value());
-              if (delta_ms > 0 && delta_ms < 60000) {
-                Accumulate(s.mIsRemote?
-                           (isAudio? WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS :
-                                     WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS) :
-                           (isAudio? WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS :
-                                     WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS),
-                           ((s.mBytesReceived.Value() -
-                             lasts.mBytesReceived.Value()) * 8) / delta_ms);
+              // In theory we're called every second, so delta *should* be in that range.
+              // Small deltas could cause errors due to division
+              if (delta_ms > 500 && delta_ms < 60000) {
+                ID id;
+                if (s.mIsRemote) {
+                  if (isAudio) {
+                    id = isHello ? LOOP_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS :
+                                   WEBRTC_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS;
+                  } else {
+                    id = isHello ? LOOP_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS :
+                                   WEBRTC_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS;
+                  }
+                } else {
+                  if (isAudio) {
+                    id = isHello ? LOOP_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS :
+                                   WEBRTC_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS;
+                  } else {
+                    id = isHello ? LOOP_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS :
+                                   WEBRTC_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS;
+                  }
+                }
+                Accumulate(id, ((s.mBytesReceived.Value() -
+                                 lasts.mBytesReceived.Value()) * 8) / delta_ms);
               }
+              // We could accumulate values until enough time has passed
+              // and then Accumulate() but this isn't that important.
             }
           }
         }
       }
     }
   }
   // Steal and hang on to reports for the next second
   ctx->mLastReports.Clear();
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -375,16 +375,17 @@ PeerConnectionImpl::PeerConnectionImpl(c
   , mDtlsConnected(false)
   , mWindow(nullptr)
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   , mCertificate(nullptr)
 #else
   , mIdentity(nullptr)
 #endif
   , mPrivacyRequested(false)
+  , mIsLoop(false)
   , mSTSThread(nullptr)
   , mAllowIceLoopback(false)
   , mAllowIceLinkLocal(false)
   , mMedia(nullptr)
   , mUuidGen(MakeUnique<PCUuidGenerator>())
   , mNumAudioStreams(0)
   , mNumVideoStreams(0)
   , mHaveDataStream(false)
@@ -688,16 +689,22 @@ PeerConnectionImpl::Initialize(PeerConne
   res = mWindow->GetLocation(&location);
 
   if (location && NS_SUCCEEDED(res)) {
     nsAutoString locationAStr;
     location->ToString(locationAStr);
     location->Release();
 
     CopyUTF16toUTF8(locationAStr, locationCStr);
+#define HELLO_CLICKER_URL_START "https://hello.firefox.com/"
+#define HELLO_INITIATOR_URL_START "about:loop"
+    mIsLoop = (strncmp(HELLO_CLICKER_URL_START, locationCStr.get(),
+                       strlen(HELLO_CLICKER_URL_START)) == 0) ||
+              (strncmp(HELLO_INITIATOR_URL_START, locationCStr.get(),
+                       strlen(HELLO_INITIATOR_URL_START)) == 0);
   }
 
   PR_snprintf(
       temp,
       sizeof(temp),
       "%llu (id=%llu url=%s)",
       static_cast<unsigned long long>(timestamp),
       static_cast<unsigned long long>(mWindow ? mWindow->WindowID() : 0),
@@ -2477,17 +2484,19 @@ PeerConnectionImpl::ShutdownMedia()
   for(uint32_t i = 0; i < media()->LocalStreamsLength(); ++i) {
     LocalSourceStreamInfo *info = media()->GetLocalStreamByIndex(i);
     info->GetMediaStream()->RemovePrincipalChangeObserver(this);
   }
 
   // End of call to be recorded in Telemetry
   if (!mStartTime.IsNull()){
     TimeDuration timeDelta = TimeStamp::Now() - mStartTime;
-    Telemetry::Accumulate(Telemetry::WEBRTC_CALL_DURATION, timeDelta.ToSeconds());
+    Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_CALL_DURATION :
+                                    Telemetry::WEBRTC_CALL_DURATION,
+                          timeDelta.ToSeconds());
   }
 #endif
 
   // Forget the reference so that we can transfer it to
   // SelfDestruct().
   mMedia.forget().take()->SelfDestruct();
 }
 
@@ -2759,20 +2768,22 @@ void PeerConnectionImpl::IceConnectionSt
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   if (!isDone(mIceConnectionState) && isDone(domState)) {
     // mIceStartTime can be null if going directly from New to Closed, in which
     // case we don't count it as a success or a failure.
     if (!mIceStartTime.IsNull()){
       TimeDuration timeDelta = TimeStamp::Now() - mIceStartTime;
       if (isSucceeded(domState)) {
-        Telemetry::Accumulate(Telemetry::WEBRTC_ICE_SUCCESS_TIME,
+        Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_ICE_SUCCESS_TIME :
+                                        Telemetry::WEBRTC_ICE_SUCCESS_TIME,
                               timeDelta.ToMilliseconds());
       } else if (isFailed(domState)) {
-        Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FAILURE_TIME,
+        Telemetry::Accumulate(mIsLoop ? Telemetry::LOOP_ICE_FAILURE_TIME :
+                                        Telemetry::WEBRTC_ICE_FAILURE_TIME,
                               timeDelta.ToMilliseconds());
       }
     }
 
     if (isSucceeded(domState)) {
       Telemetry::Accumulate(
           Telemetry::WEBRTC_ICE_ADD_CANDIDATE_ERRORS_GIVEN_SUCCESS,
           mAddCandidateErrorCount);
@@ -2922,16 +2933,17 @@ PeerConnectionImpl::BuildStatsQuery_m(
 
   // We do not use the pcHandle here, since that's risky to expose to content.
   query->report = new RTCStatsReportInternalConstruct(
       NS_ConvertASCIItoUTF16(mName.c_str()),
       query->now);
 
   query->iceStartTime = mIceStartTime;
   query->failed = isFailed(mIceConnectionState);
+  query->isHello = mIsLoop;
 
   // Populate SDP on main
   if (query->internalStats) {
     if (mJsepSession) {
       std::string localDescription = mJsepSession->GetLocalDescription();
       std::string remoteDescription = mJsepSession->GetRemoteDescription();
       query->report->mLocalSdp.Construct(
           NS_ConvertASCIItoUTF16(localDescription.c_str()));
@@ -3380,18 +3392,21 @@ PeerConnectionImpl::startCallTelem() {
   if (!mStartTime.IsNull()) {
     return;
   }
 
   // Start time for calls
   mStartTime = TimeStamp::Now();
 
   // Increment session call counter
+  // If we want to track Loop calls independently here, we need two mConnectionCounters
   int &cnt = PeerConnectionCtx::GetInstance()->mConnectionCounter;
-  Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Subtract(cnt);
+  if (cnt > 0) {
+    Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Subtract(cnt);
+  }
   cnt++;
   Telemetry::GetHistogramById(Telemetry::WEBRTC_CALL_COUNT)->Add(cnt);
 }
 #endif
 
 NS_IMETHODIMP
 PeerConnectionImpl::GetLocalStreams(nsTArray<nsRefPtr<DOMMediaStream > >& result)
 {
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -209,16 +209,17 @@ class RTCStatsQuery {
   public:
     explicit RTCStatsQuery(bool internalStats);
     ~RTCStatsQuery();
 
     nsAutoPtr<mozilla::dom::RTCStatsReportInternal> report;
     std::string error;
     // A timestamp to help with telemetry.
     mozilla::TimeStamp iceStartTime;
+    bool isHello;
     // Just for convenience, maybe integrate into the report later
     bool failed;
 
   private:
     friend class PeerConnectionImpl;
     std::string pcName;
     bool internalStats;
     nsTArray<mozilla::RefPtr<mozilla::MediaPipeline>> pipelines;
@@ -471,16 +472,18 @@ public:
 
   nsresult SetId(const nsAString& id)
   {
     mName = NS_ConvertUTF16toUTF8(id).get();
     return NS_OK;
   }
 #endif
 
+  bool IsLoop() const { return mIsLoop; }
+
   // this method checks to see if we've made a promise to protect media.
   bool PrivacyRequested() const { return mPrivacyRequested; }
 
   NS_IMETHODIMP GetFingerprint(char** fingerprint);
   void GetFingerprint(nsAString& fingerprint)
   {
     char *tmp;
     GetFingerprint(&tmp);
@@ -738,16 +741,17 @@ private:
   // provided, but the media is not protected from the app on either side
   bool mPrivacyRequested;
 
   // A handle to refer to this PC with
   std::string mHandle;
 
   // A name for this PC that we are willing to expose to content.
   std::string mName;
+  bool mIsLoop; // For telemetry; doesn't have to be 100% right
 
   // The target to run stuff on
   nsCOMPtr<nsIEventTarget> mSTSThread;
 
 #if !defined(MOZILLA_EXTERNAL_LINKAGE)
   // DataConnection that's used to get all the DataChannels
   nsRefPtr<mozilla::DataChannelConnection> mDataConnection;
 #endif
--- a/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
+++ b/media/webrtc/signaling/src/peerconnection/WebrtcGlobalInformation.cpp
@@ -820,17 +820,18 @@ MOZ_IMPLICIT WebrtcGlobalChild::~WebrtcG
 struct StreamResult {
   StreamResult() : candidateTypeBitpattern(0), streamSucceeded(false) {}
   uint8_t candidateTypeBitpattern;
   bool streamSucceeded;
 };
 
 static void StoreLongTermICEStatisticsImpl_m(
     nsresult result,
-    nsAutoPtr<RTCStatsQuery> query) {
+    nsAutoPtr<RTCStatsQuery> query,
+    bool aIsLoop) {
 
   using namespace Telemetry;
 
   if (NS_FAILED(result) ||
       !query->error.empty() ||
       !query->report->mIceCandidateStats.WasPassed()) {
     return;
   }
@@ -920,104 +921,117 @@ static void StoreLongTermICEStatisticsIm
         streamResults[streamId].candidateTypeBitpattern |=
           REMOTE_GATHERED_SERVER_REFLEXIVE;
       }
     }
   }
 
   for (auto i = streamResults.begin(); i != streamResults.end(); ++i) {
     if (i->second.streamSucceeded) {
-      Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS,
+      Telemetry::Accumulate(aIsLoop ? Telemetry::LOOP_CANDIDATE_TYPES_GIVEN_SUCCESS :
+                                      Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_SUCCESS,
                             i->second.candidateTypeBitpattern);
     } else {
-      Telemetry::Accumulate(Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE,
+      Telemetry::Accumulate(aIsLoop ? Telemetry::LOOP_CANDIDATE_TYPES_GIVEN_FAILURE :
+                                      Telemetry::WEBRTC_CANDIDATE_TYPES_GIVEN_FAILURE,
                             i->second.candidateTypeBitpattern);
     }
   }
 
   // Beyond ICE, accumulate telemetry for various PER_CALL settings here.
 
   if (query->report->mOutboundRTPStreamStats.WasPassed()) {
     auto& array = query->report->mOutboundRTPStreamStats.Value();
     for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
       auto& s = array[i];
       bool isVideo = (s.mId.Value().Find("video") != -1);
       if (!isVideo || s.mIsRemote) {
         continue;
       }
       if (s.mBitrateMean.WasPassed()) {
-        Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS,
+        Accumulate(aIsLoop ? LOOP_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS :
+                             WEBRTC_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS,
                    uint32_t(s.mBitrateMean.Value() / 1000));
       }
       if (s.mBitrateStdDev.WasPassed()) {
-        Accumulate(WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS,
+        Accumulate(aIsLoop? LOOP_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS :
+                            WEBRTC_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS,
                    uint32_t(s.mBitrateStdDev.Value() / 1000));
       }
       if (s.mFramerateMean.WasPassed()) {
-        Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL,
+        Accumulate(aIsLoop ? LOOP_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL :
+                             WEBRTC_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL,
                    uint32_t(s.mFramerateMean.Value()));
       }
       if (s.mFramerateStdDev.WasPassed()) {
-        Accumulate(WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL,
+        Accumulate(aIsLoop ? LOOP_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL :
+                             WEBRTC_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL,
                    uint32_t(s.mFramerateStdDev.Value() * 10));
       }
       if (s.mDroppedFrames.WasPassed() && !query->iceStartTime.IsNull()) {
         double mins = (TimeStamp::Now() - query->iceStartTime).ToSeconds() / 60;
         if (mins > 0) {
-          Accumulate(WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM,
+          Accumulate(aIsLoop ? LOOP_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM :
+                               WEBRTC_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM,
                      uint32_t(double(s.mDroppedFrames.Value()) / mins));
         }
       }
     }
   }
 
   if (query->report->mInboundRTPStreamStats.WasPassed()) {
     auto& array = query->report->mInboundRTPStreamStats.Value();
     for (decltype(array.Length()) i = 0; i < array.Length(); i++) {
       auto& s = array[i];
       bool isVideo = (s.mId.Value().Find("video") != -1);
       if (!isVideo || s.mIsRemote) {
         continue;
       }
       if (s.mBitrateMean.WasPassed()) {
-        Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS,
+        Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS :
+                             WEBRTC_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS,
                    uint32_t(s.mBitrateMean.Value() / 1000));
       }
       if (s.mBitrateStdDev.WasPassed()) {
-        Accumulate(WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS,
+        Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS :
+                             WEBRTC_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS,
                    uint32_t(s.mBitrateStdDev.Value() / 1000));
       }
       if (s.mFramerateMean.WasPassed()) {
-        Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL,
+        Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL :
+                             WEBRTC_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL,
                    uint32_t(s.mFramerateMean.Value()));
       }
       if (s.mFramerateStdDev.WasPassed()) {
-        Accumulate(WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL,
+        Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL :
+                             WEBRTC_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL,
                    uint32_t(s.mFramerateStdDev.Value() * 10));
       }
       if (s.mDiscardedPackets.WasPassed() && !query->iceStartTime.IsNull()) {
         double mins = (TimeStamp::Now() - query->iceStartTime).ToSeconds() / 60;
         if (mins > 0) {
-          Accumulate(WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM,
+          Accumulate(aIsLoop ? LOOP_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM :
+                               WEBRTC_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM,
                      uint32_t(double(s.mDiscardedPackets.Value()) / mins));
         }
       }
     }
   }
 
   // Finally, store the stats
 
   PeerConnectionCtx *ctx = GetPeerConnectionCtx();
   if (ctx) {
     ctx->mStatsForClosedPeerConnections.AppendElement(*query->report, fallible);
   }
 }
 
 static void GetStatsForLongTermStorage_s(
-    nsAutoPtr<RTCStatsQuery> query) {
+    nsAutoPtr<RTCStatsQuery> query,
+    bool aIsLoop) {
 
   MOZ_ASSERT(query);
 
   nsresult rv = PeerConnectionImpl::ExecuteStatsQuery_s(query.get());
 
   // Check whether packets were dropped due to rate limiting during
   // this call. (These calls must be made on STS)
   unsigned char rate_limit_bit_pattern = 0;
@@ -1043,37 +1057,39 @@ static void GetStatsForLongTermStorage_s
   }
 
   // Even if Telemetry::Accumulate is threadsafe, we still need to send the
   // query back to main, since that is where it must be destroyed.
   NS_DispatchToMainThread(
       WrapRunnableNM(
           &StoreLongTermICEStatisticsImpl_m,
           rv,
-          query),
+          query,
+          aIsLoop),
       NS_DISPATCH_NORMAL);
 }
 
 void WebrtcGlobalInformation::StoreLongTermICEStatistics(
     PeerConnectionImpl& aPc) {
-  Telemetry::Accumulate(Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE,
+  Telemetry::Accumulate(aPc.IsLoop() ? Telemetry::LOOP_ICE_FINAL_CONNECTION_STATE :
+                                       Telemetry::WEBRTC_ICE_FINAL_CONNECTION_STATE,
                         static_cast<uint32_t>(aPc.IceConnectionState()));
 
   if (aPc.IceConnectionState() == PCImplIceConnectionState::New) {
     // ICE has not started; we won't have any remote candidates, so recording
     // statistics on gathered candidates is pointless.
     return;
   }
 
   nsAutoPtr<RTCStatsQuery> query(new RTCStatsQuery(true));
 
   nsresult rv = aPc.BuildStatsQuery_m(nullptr, query.get());
 
   NS_ENSURE_SUCCESS_VOID(rv);
 
   RUN_ON_THREAD(aPc.GetSTSThread(),
                 WrapRunnableNM(&GetStatsForLongTermStorage_s,
-                               query),
+                               query, aPc.IsLoop()),
                 NS_DISPATCH_NORMAL);
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/toolkit/components/telemetry/Histograms.json
+++ b/toolkit/components/telemetry/Histograms.json
@@ -8666,10 +8666,208 @@
     "n_values": 10,
     "description": "Reports a comparison between row count of original and re-migration of the v7 permissions DB. 0=New == 0, 1=New < Old, 2=New == Old, 3=New > Old"
   },
   "PERMISSIONS_MIGRATION_7_ERROR": {
     "alert_emails": ["michael@thelayzells.com"],
     "expires_in_version": "44",
     "kind": "boolean",
     "description": "Was there an error while performing the v7 permissions DB migration?"
+  },
+  "LOOP_ICE_FINAL_CONNECTION_STATE": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 7,
+    "description": "The ICE connection state when the PC was closed"
+  },
+  "LOOP_ICE_SUCCESS_TIME": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "20",
+    "description": "The length of time (in milliseconds) it took for ICE to complete, given that ICE succeeded."
+  },
+  "LOOP_ICE_FAILURE_TIME": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "20",
+    "description": "The length of time (in milliseconds) it took for ICE to complete, given that it failed."
+  },
+  "LOOP_ICE_SUCCESS_RATE": {
+    "expires_in_version": "never",
+    "kind": "boolean",
+    "description": "The number of failed ICE Connections (0) vs. number of successful ICE connections (1)."
+  },
+  "LOOP_CANDIDATE_TYPES_GIVEN_SUCCESS": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 128,
+    "description": "A bitpattern indicating what types of candidates were present. Bit 0: Remote server reflexive. Bit 1: Remote relayed. Bit 2: Local server reflexive. Bits 3-6: Local UDP, TCP, TLS, and HTTPS relay respectively."
+  },
+  "LOOP_CANDIDATE_TYPES_GIVEN_FAILURE": {
+    "expires_in_version": "never",
+    "kind": "enumerated",
+    "n_values": 128,
+    "description": "Identical to LOOP_CANDIDATE_TYPES_GIVEN_SUCCEESS, except recorded only when ICE fails on the stream in question."
+  },
+  "LOOP_VIDEO_QUALITY_INBOUND_BANDWIDTH_KBITS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000000,
+    "n_buckets": 1000,
+    "description": "Locally measured data rate of inbound video (kbit/s). Computed every second of a call."
+  },
+  "LOOP_AUDIO_QUALITY_INBOUND_BANDWIDTH_KBITS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000000,
+    "n_buckets": 1000,
+    "description": "Locally measured data rate on inbound audio (kbit/s). Computed every second of a call."
+  },
+  "LOOP_VIDEO_QUALITY_OUTBOUND_BANDWIDTH_KBITS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000000,
+    "n_buckets": 1000,
+    "description": "Data rate deduced from RTCP from remote recipient of outbound video (kbit/s). Computed every second of a call (for easy comparison)."
+  },
+  "LOOP_AUDIO_QUALITY_OUTBOUND_BANDWIDTH_KBITS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000000,
+    "n_buckets": 1000,
+    "description": "Data rate deduced from RTCP from remote recipient of outbound audio (kbit/s). Computed every second of a call (for easy comparison)."
+  },
+  "LOOP_VIDEO_ERROR_RECOVERY_MS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 500,
+    "description": "Time to recover from a video error in ms"
+  },
+  "LOOP_VIDEO_RECOVERY_BEFORE_ERROR_PER_MIN": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000,
+    "n_buckets": 200,
+    "description": "Number of losses recovered before error per min"
+  },
+  "LOOP_VIDEO_RECOVERY_AFTER_ERROR_PER_MIN": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000,
+    "n_buckets": 200,
+    "description": "Number of losses recovered after error per min"
+  },
+  "LOOP_VIDEO_DECODE_ERROR_TIME_PERMILLE": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 1000,
+    "n_buckets": 100,
+    "description": "Percentage*10 (permille) of call decoding with errors or frozen due to errors"
+  },
+  "LOOP_VIDEO_QUALITY_OUTBOUND_RTT": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 1000,
+    "description": "Roundtrip time of outbound video (ms). Sampled every second of a call."
+  },
+  "LOOP_AUDIO_QUALITY_OUTBOUND_RTT": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 1000,
+    "description": "Roundtrip time of outbound audio (ms). Sampled every second of a call."
+  },
+  "LOOP_VIDEO_ENCODER_BITRATE_AVG_PER_CALL_KBPS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 100,
+    "description": "Video encoder's average bitrate (in kbits/s) over an entire call"
+  },
+  "LOOP_VIDEO_ENCODER_BITRATE_STD_DEV_PER_CALL_KBPS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 5000,
+    "n_buckets": 100,
+    "description": "Standard deviation from video encoder's average bitrate (in kbits/s) over an entire call"
+  },
+  "LOOP_VIDEO_ENCODER_FRAMERATE_AVG_PER_CALL": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 200,
+    "n_buckets": 50,
+    "description": "Video encoder's average framerate (in fps) over an entire call"
+  },
+  "LOOP_VIDEO_ENCODER_FRAMERATE_10X_STD_DEV_PER_CALL": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 200,
+    "n_buckets": 50,
+    "description": "Standard deviation from video encoder's average framerate (in 1/10 fps) over an entire call"
+  },
+  "LOOP_VIDEO_ENCODER_DROPPED_FRAMES_PER_CALL_FPM": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 50000,
+    "n_buckets": 100,
+    "description": "Video encoder's number of frames dropped (in frames/min) over an entire call"
+  },
+  "LOOP_VIDEO_DECODER_BITRATE_AVG_PER_CALL_KBPS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 10000,
+    "n_buckets": 100,
+    "description": "Video decoder's average bitrate (in kbits/s) over an entire call"
+  },
+  "LOOP_VIDEO_DECODER_BITRATE_STD_DEV_PER_CALL_KBPS": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 5000,
+    "n_buckets": 100,
+    "description": "Standard deviation from video decoder's average bitrate (in kbits/s) over an entire call"
+  },
+  "LOOP_VIDEO_DECODER_FRAMERATE_AVG_PER_CALL": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "200",
+    "n_buckets": "50",
+    "description": "Video decoder's average framerate (in fps) over an entire call"
+  },
+  "LOOP_VIDEO_DECODER_FRAMERATE_10X_STD_DEV_PER_CALL": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "200",
+    "n_buckets": "50",
+    "description": "Standard deviation from video decoder's average framerate (in 1/10 fps) over an entire call"
+  },
+  "LOOP_VIDEO_DECODER_DISCARDED_PACKETS_PER_CALL_PPM": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": 50000,
+    "n_buckets": 100,
+    "description": "Video decoder's number of discarded packets (in packets/min) over an entire call"
+  },
+  "LOOP_CALL_DURATION": {
+    "expires_in_version": "never",
+    "kind": "exponential",
+    "high": "10000",
+    "n_buckets": "1000",
+    "description": "The length of time (in seconds) that a call lasted."
+  },
+  "LOOP_ICE_ADD_CANDIDATE_ERRORS_GIVEN_SUCCESS": {
+    "expires_in_version": "never",
+    "kind": "linear",
+    "high": "30",
+    "n_buckets": "29",
+    "description": "The number of times AddIceCandidate failed on a given PeerConnection, given that ICE succeeded."
+  },
+  "LOOP_ICE_ADD_CANDIDATE_ERRORS_GIVEN_FAILURE": {
+    "expires_in_version": "never",
+    "kind": "linear",
+    "high": "30",
+    "n_buckets": "29",
+    "description": "The number of times AddIceCandidate failed on a given PeerConnection, given that ICE failed."
   }
 }