diff options
Diffstat (limited to 'toolkit/components/telemetry/geckoview')
5 files changed, 615 insertions, 0 deletions
diff --git a/toolkit/components/telemetry/geckoview/gtest/TestGeckoViewStreaming.cpp b/toolkit/components/telemetry/geckoview/gtest/TestGeckoViewStreaming.cpp new file mode 100644 index 0000000000..ebaa7099a8 --- /dev/null +++ b/toolkit/components/telemetry/geckoview/gtest/TestGeckoViewStreaming.cpp @@ -0,0 +1,237 @@ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "mozilla/gtest/MozAssertions.h" +#include "mozilla/Preferences.h" +#include "mozilla/Services.h" +#include "mozilla/Telemetry.h" +#include "nsIObserverService.h" +#include "nsThreadUtils.h" +#include "TelemetryFixture.h" +#include "TelemetryTestHelpers.h" +#include "streaming/GeckoViewStreamingTelemetry.h" + +using namespace mozilla; +using namespace mozilla::Telemetry; +using namespace TelemetryTestHelpers; +using GeckoViewStreamingTelemetry::StreamingTelemetryDelegate; +using mozilla::Telemetry::ScalarID; +using ::testing::_; +using ::testing::Eq; +using ::testing::StrictMock; + +namespace { + +const char* kGeckoViewStreamingPref = "toolkit.telemetry.geckoview.streaming"; +const char* kBatchTimeoutPref = "toolkit.telemetry.geckoview.batchDurationMS"; + +constexpr auto kTestHgramName = "TELEMETRY_TEST_STREAMING"_ns; +constexpr auto kTestHgramName2 = "TELEMETRY_TEST_STREAMING_2"_ns; +constexpr auto kTestCategoricalName = "TELEMETRY_TEST_CATEGORICAL_OPTOUT"_ns; +const HistogramID kTestHgram = Telemetry::TELEMETRY_TEST_STREAMING; +const HistogramID kTestHgram2 = Telemetry::TELEMETRY_TEST_STREAMING_2; + +class TelemetryStreamingFixture : public TelemetryTestFixture { + protected: + virtual void SetUp() { + TelemetryTestFixture::SetUp(); + Preferences::SetBool(kGeckoViewStreamingPref, true); + Preferences::SetInt(kBatchTimeoutPref, 5000); + } + virtual void TearDown() { + TelemetryTestFixture::TearDown(); + Preferences::SetBool(kGeckoViewStreamingPref, false); + GeckoViewStreamingTelemetry::RegisterDelegate(nullptr); + } +}; + +class MockDelegate : public StreamingTelemetryDelegate { + public: + MOCK_METHOD2(ReceiveHistogramSamples, + void(const nsCString& aHistogramName, + const nsTArray<uint32_t>& aSamples)); + MOCK_METHOD2(ReceiveCategoricalHistogramSamples, + void(const nsCString& aHistogramName, + const nsTArray<uint32_t>& aSamples)); + MOCK_METHOD2(ReceiveBoolScalarValue, + void(const nsCString& aScalarName, bool aValue)); + MOCK_METHOD2(ReceiveStringScalarValue, + void(const nsCString& aScalarName, const nsCString& aValue)); + MOCK_METHOD2(ReceiveUintScalarValue, + void(const nsCString& aScalarName, uint32_t aValue)); +}; // class MockDelegate + +TEST_F(TelemetryStreamingFixture, HistogramSamples) { + const uint32_t kSampleOne = 401; + const uint32_t kSampleTwo = 2019; + + CopyableTArray<uint32_t> samplesArray; + samplesArray.AppendElement(kSampleOne); + samplesArray.AppendElement(kSampleTwo); + + auto md = MakeRefPtr<MockDelegate>(); + EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), + Eq(std::move(samplesArray)))); + GeckoViewStreamingTelemetry::RegisterDelegate(md); + + Telemetry::Accumulate(Telemetry::TELEMETRY_TEST_STREAMING, kSampleOne); + Preferences::SetInt(kBatchTimeoutPref, 0); + Telemetry::Accumulate(Telemetry::TELEMETRY_TEST_STREAMING, kSampleTwo); +} + +TEST_F(TelemetryStreamingFixture, CategoricalHistogramSamples) { + auto kSampleOne = + Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL_OPTOUT::CommonLabel; + auto kSampleTwo = Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL_OPTOUT::Label5; + + CopyableTArray<uint32_t> samplesArray; + samplesArray.AppendElement(static_cast<uint32_t>(kSampleOne)); + samplesArray.AppendElement(static_cast<uint32_t>(kSampleOne)); + samplesArray.AppendElement(static_cast<uint32_t>(kSampleTwo)); + + auto md = MakeRefPtr<MockDelegate>(); + EXPECT_CALL(*md, ReceiveCategoricalHistogramSamples( + Eq(kTestCategoricalName), Eq(std::move(samplesArray)))); + GeckoViewStreamingTelemetry::RegisterDelegate(md); + + Telemetry::AccumulateCategorical(kSampleOne); + Telemetry::AccumulateCategorical(kSampleOne); + Preferences::SetInt(kBatchTimeoutPref, 0); + Telemetry::AccumulateCategorical(kSampleTwo); +} + +TEST_F(TelemetryStreamingFixture, MultipleHistograms) { + const uint32_t kSample1 = 400; + const uint32_t kSample2 = 1 << 31; + const uint32_t kSample3 = 7; + CopyableTArray<uint32_t> samplesArray1; + samplesArray1.AppendElement(kSample1); + samplesArray1.AppendElement(kSample2); + CopyableTArray<uint32_t> samplesArray2; + samplesArray2.AppendElement(kSample3); + + auto md = MakeRefPtr<MockDelegate>(); + EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), + Eq(std::move(samplesArray1)))); + EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName2), + Eq(std::move(samplesArray2)))); + + GeckoViewStreamingTelemetry::RegisterDelegate(md); + + Telemetry::Accumulate(kTestHgram, kSample1); + Telemetry::Accumulate(kTestHgram2, kSample3); + Preferences::SetInt(kBatchTimeoutPref, 0); + Telemetry::Accumulate(kTestHgram, kSample2); +} + +// If we can find a way to convert the expectation's arg into an stl container, +// we can use gmock's own ::testing::UnorderedElementsAre() instead. +auto MatchUnordered(uint32_t sample1, uint32_t sample2) { + CopyableTArray<uint32_t> samplesArray1; + samplesArray1.AppendElement(sample1); + samplesArray1.AppendElement(sample2); + + CopyableTArray<uint32_t> samplesArray2; + samplesArray2.AppendElement(sample2); + samplesArray2.AppendElement(sample1); + + return ::testing::AnyOf(Eq(std::move(samplesArray1)), + Eq(std::move(samplesArray2))); +} + +TEST_F(TelemetryStreamingFixture, MultipleThreads) { + const uint32_t kSample1 = 4; + const uint32_t kSample2 = 14; + + auto md = MakeRefPtr<MockDelegate>(); + // In this test, samples for the second test hgram are uninteresting. + EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName2), _)); + EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), + MatchUnordered(kSample1, kSample2))); + + GeckoViewStreamingTelemetry::RegisterDelegate(md); + + nsCOMPtr<nsIThread> t1; + nsCOMPtr<nsIThread> t2; + nsCOMPtr<nsIThread> t3; + + nsCOMPtr<nsIRunnable> r1 = NS_NewRunnableFunction( + "accumulate 4", [&]() { Telemetry::Accumulate(kTestHgram, kSample1); }); + nsCOMPtr<nsIRunnable> r2 = NS_NewRunnableFunction( + "accumulate 14", [&]() { Telemetry::Accumulate(kTestHgram, kSample2); }); + + nsresult rv = NS_NewNamedThread("t1", getter_AddRefs(t1), r1); + EXPECT_NS_SUCCEEDED(rv); + rv = NS_NewNamedThread("t2", getter_AddRefs(t2), r2); + EXPECT_NS_SUCCEEDED(rv); + + // Give the threads a chance to do their work. + PR_Sleep(PR_MillisecondsToInterval(1)); + + Preferences::SetInt(kBatchTimeoutPref, 0); + Telemetry::Accumulate(kTestHgram2, kSample1); +} + +TEST_F(TelemetryStreamingFixture, ScalarValues) { + constexpr auto kBoolScalarName = "telemetry.test.boolean_kind"_ns; + constexpr auto kStringScalarName = "telemetry.test.string_kind"_ns; + constexpr auto kUintScalarName = "telemetry.test.unsigned_int_kind"_ns; + + const bool kBoolScalarValue = true; + constexpr auto kStringScalarValue = "a string scalar value"_ns; + const uint32_t kUintScalarValue = 42; + + auto md = MakeRefPtr<MockDelegate>(); + EXPECT_CALL( + *md, ReceiveBoolScalarValue(Eq(kBoolScalarName), Eq(kBoolScalarValue))); + EXPECT_CALL(*md, ReceiveStringScalarValue(Eq(kStringScalarName), + Eq(kStringScalarValue))); + EXPECT_CALL( + *md, ReceiveUintScalarValue(Eq(kUintScalarName), Eq(kUintScalarValue))); + + GeckoViewStreamingTelemetry::RegisterDelegate(md); + + Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, kBoolScalarValue); + Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_STRING_KIND, + NS_ConvertUTF8toUTF16(kStringScalarValue)); + Preferences::SetInt(kBatchTimeoutPref, + 0); // Trigger batch on next accumulation. + Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, + kUintScalarValue); +} + +TEST_F(TelemetryStreamingFixture, ExpiredHistogram) { + const HistogramID kExpiredHistogram = Telemetry::TELEMETRY_TEST_EXPIRED; + const uint32_t kSample = 401; + + // Strict Mock fails on any method calls. + auto md = MakeRefPtr<StrictMock<MockDelegate>>(); + GeckoViewStreamingTelemetry::RegisterDelegate(md); + + Preferences::SetInt(kBatchTimeoutPref, 0); + Telemetry::Accumulate(kExpiredHistogram, kSample); +} + +TEST_F(TelemetryStreamingFixture, SendOnAppBackground) { + constexpr auto kBoolScalarName = "telemetry.test.boolean_kind"_ns; + const bool kBoolScalarValue = true; + const char* kApplicationBackgroundTopic = "application-background"; + + auto md = MakeRefPtr<MockDelegate>(); + EXPECT_CALL( + *md, ReceiveBoolScalarValue(Eq(kBoolScalarName), Eq(kBoolScalarValue))); + + GeckoViewStreamingTelemetry::RegisterDelegate(md); + Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, kBoolScalarValue); + + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); + ASSERT_TRUE(!!os) + << "Observer Service unavailable?!?!"; + os->NotifyObservers(nullptr, kApplicationBackgroundTopic, nullptr); +} + +} // namespace diff --git a/toolkit/components/telemetry/geckoview/gtest/moz.build b/toolkit/components/telemetry/geckoview/gtest/moz.build new file mode 100644 index 0000000000..eb6a2f9293 --- /dev/null +++ b/toolkit/components/telemetry/geckoview/gtest/moz.build @@ -0,0 +1,28 @@ +# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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 +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +Library("telemetrygeckoviewtest") + +LOCAL_INCLUDES += [ + "../", + "../..", + "../../..", + "/toolkit/components/telemetry/tests/gtest", + "/xpcom/io", +] + +# GeckoView Streaming Telemetry is only available on Android. +if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": + UNIFIED_SOURCES += [ + "TestGeckoViewStreaming.cpp", + ] + +# We need the following line otherwise including +# "TelemetryHistogram.h" in tests will fail due to +# missing headers. +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" diff --git a/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.cpp b/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.cpp new file mode 100644 index 0000000000..6c4b9590c0 --- /dev/null +++ b/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.cpp @@ -0,0 +1,282 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "GeckoViewStreamingTelemetry.h" + +#include "mozilla/Assertions.h" +#include "mozilla/Services.h" +#include "mozilla/StaticMutex.h" +#include "mozilla/StaticPtr.h" +#include "mozilla/StaticPrefs_toolkit.h" +#include "mozilla/TimeStamp.h" +#include "nsTHashMap.h" +#include "nsIObserver.h" +#include "nsIObserverService.h" +#include "nsITimer.h" +#include "nsTArray.h" +#include "nsThreadUtils.h" + +using mozilla::Runnable; +using mozilla::StaticMutex; +using mozilla::StaticMutexAutoLock; +using mozilla::StaticRefPtr; +using mozilla::TimeStamp; + +// Batches and streams Telemetry samples to a JNI delegate which will +// (presumably) do something with the data. Expected to be used to route data +// up to the Android Components layer to be translated into Glean metrics. +namespace GeckoViewStreamingTelemetry { + +class LifecycleObserver; +void SendBatch(const StaticMutexAutoLock& aLock); + +// Topic on which we flush the batch. +static const char* const kApplicationBackgroundTopic = "application-background"; + +static StaticMutex gMutex MOZ_UNANNOTATED; + +// -- The following state is accessed across threads. +// -- Do not touch these if you do not hold gMutex. + +// The time the batch began. +TimeStamp gBatchBegan; +// The batch of histograms and samples. +typedef nsTHashMap<nsCStringHashKey, nsTArray<uint32_t>> HistogramBatch; +HistogramBatch gBatch; +HistogramBatch gCategoricalBatch; +// The batches of Scalars and their values. +typedef nsTHashMap<nsCStringHashKey, bool> BoolScalarBatch; +BoolScalarBatch gBoolScalars; +typedef nsTHashMap<nsCStringHashKey, nsCString> StringScalarBatch; +StringScalarBatch gStringScalars; +typedef nsTHashMap<nsCStringHashKey, uint32_t> UintScalarBatch; +UintScalarBatch gUintScalars; +// The delegate to receive the samples and values. +StaticRefPtr<StreamingTelemetryDelegate> gDelegate; +// Lifecycle observer used to flush the batch when backgrounded. +StaticRefPtr<LifecycleObserver> gObserver; + +// -- End of gMutex-protected thread-unsafe-accessed data + +// Timer that ensures data in the batch never gets too stale. +// This timer may only be manipulated on the Main Thread. +StaticRefPtr<nsITimer> gJICTimer; + +class LifecycleObserver final : public nsIObserver { + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIOBSERVER + + LifecycleObserver() = default; + + protected: + ~LifecycleObserver() = default; +}; + +NS_IMPL_ISUPPORTS(LifecycleObserver, nsIObserver); + +NS_IMETHODIMP +LifecycleObserver::Observe(nsISupports* aSubject, const char* aTopic, + const char16_t* aData) { + if (!strcmp(aTopic, kApplicationBackgroundTopic)) { + StaticMutexAutoLock lock(gMutex); + SendBatch(lock); + } + return NS_OK; +} + +void RegisterDelegate(const RefPtr<StreamingTelemetryDelegate>& aDelegate) { + StaticMutexAutoLock lock(gMutex); + gDelegate = aDelegate; +} + +class SendBatchRunnable : public Runnable { + public: + explicit SendBatchRunnable(RefPtr<StreamingTelemetryDelegate> aDelegate, + HistogramBatch&& aBatch, + HistogramBatch&& aCategoricalBatch, + BoolScalarBatch&& aBoolScalars, + StringScalarBatch&& aStringScalars, + UintScalarBatch&& aUintScalars) + : Runnable("SendBatchRunnable"), + mDelegate(std::move(aDelegate)), + mBatch(std::move(aBatch)), + mCategoricalBatch(std::move(aCategoricalBatch)), + mBoolScalars(std::move(aBoolScalars)), + mStringScalars(std::move(aStringScalars)), + mUintScalars(std::move(aUintScalars)) {} + + NS_IMETHOD Run() override { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(mDelegate); + + if (gJICTimer) { + gJICTimer->Cancel(); + } + + for (const auto& entry : mBatch) { + const nsCString& histogramName = PromiseFlatCString(entry.GetKey()); + const nsTArray<uint32_t>& samples = entry.GetData(); + + mDelegate->ReceiveHistogramSamples(histogramName, samples); + } + mBatch.Clear(); + + for (const auto& entry : mCategoricalBatch) { + const nsCString& histogramName = PromiseFlatCString(entry.GetKey()); + const nsTArray<uint32_t>& samples = entry.GetData(); + + mDelegate->ReceiveCategoricalHistogramSamples(histogramName, samples); + } + mCategoricalBatch.Clear(); + + for (const auto& entry : mBoolScalars) { + const nsCString& scalarName = PromiseFlatCString(entry.GetKey()); + mDelegate->ReceiveBoolScalarValue(scalarName, entry.GetData()); + } + mBoolScalars.Clear(); + + for (const auto& entry : mStringScalars) { + const nsCString& scalarName = PromiseFlatCString(entry.GetKey()); + const nsCString& scalarValue = PromiseFlatCString(entry.GetData()); + mDelegate->ReceiveStringScalarValue(scalarName, scalarValue); + } + mStringScalars.Clear(); + + for (const auto& entry : mUintScalars) { + const nsCString& scalarName = PromiseFlatCString(entry.GetKey()); + mDelegate->ReceiveUintScalarValue(scalarName, entry.GetData()); + } + mUintScalars.Clear(); + + return NS_OK; + } + + private: + RefPtr<StreamingTelemetryDelegate> mDelegate; + HistogramBatch mBatch; + HistogramBatch mCategoricalBatch; + BoolScalarBatch mBoolScalars; + StringScalarBatch mStringScalars; + UintScalarBatch mUintScalars; +}; // class SendBatchRunnable + +// Can be called on any thread. +// NOTE: Pay special attention to what you call in this method as if it +// accumulates to a gv-streaming-enabled probe we will deadlock the calling +// thread. +void SendBatch(const StaticMutexAutoLock& aLock) { + if (!gDelegate) { + NS_WARNING( + "Being asked to send Streaming Telemetry with no registered Streaming " + "Telemetry Delegate. Will try again later."); + // Give us another full Batch Duration to register a delegate. + gBatchBegan = TimeStamp::Now(); + return; + } + + // To make it so accumulations within the delegation don't deadlock us, + // move the batches' contents into the Runner. + HistogramBatch histogramCopy; + gBatch.SwapElements(histogramCopy); + HistogramBatch categoricalCopy; + gCategoricalBatch.SwapElements(categoricalCopy); + BoolScalarBatch boolScalarCopy; + gBoolScalars.SwapElements(boolScalarCopy); + StringScalarBatch stringScalarCopy; + gStringScalars.SwapElements(stringScalarCopy); + UintScalarBatch uintScalarCopy; + gUintScalars.SwapElements(uintScalarCopy); + RefPtr<SendBatchRunnable> runnable = new SendBatchRunnable( + gDelegate, std::move(histogramCopy), std::move(categoricalCopy), + std::move(boolScalarCopy), std::move(stringScalarCopy), + std::move(uintScalarCopy)); + + // To make things easier for the delegate, dispatch to the main thread. + NS_DispatchToMainThread(runnable); +} + +// Can be called on any thread. +void BatchCheck(const StaticMutexAutoLock& aLock) { + if (!gObserver) { + gObserver = new LifecycleObserver(); + nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); + if (os) { + os->AddObserver(gObserver, kApplicationBackgroundTopic, false); + } + } + if (gBatchBegan.IsNull()) { + // Time to begin a new batch. + gBatchBegan = TimeStamp::Now(); + // Set a just-in-case timer to enforce an upper-bound on batch staleness. + NS_DispatchToMainThread(NS_NewRunnableFunction( + "GeckoviewStreamingTelemetry::ArmTimer", []() -> void { + if (!gJICTimer) { + gJICTimer = NS_NewTimer().take(); + } + if (gJICTimer) { + gJICTimer->InitWithNamedFuncCallback( + [](nsITimer*, void*) -> void { + StaticMutexAutoLock locker(gMutex); + SendBatch(locker); + }, + nullptr, + mozilla::StaticPrefs:: + toolkit_telemetry_geckoview_maxBatchStalenessMS(), + nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, + "GeckoviewStreamingTelemetry::SendBatch"); + } + })); + } + double batchDurationMs = (TimeStamp::Now() - gBatchBegan).ToMilliseconds(); + if (batchDurationMs > + mozilla::StaticPrefs::toolkit_telemetry_geckoview_batchDurationMS()) { + SendBatch(aLock); + gBatchBegan = TimeStamp(); + } +} + +// Can be called on any thread. +void HistogramAccumulate(const nsCString& aName, bool aIsCategorical, + uint32_t aValue) { + StaticMutexAutoLock lock(gMutex); + + if (aIsCategorical) { + nsTArray<uint32_t>& samples = gCategoricalBatch.LookupOrInsert(aName); + samples.AppendElement(aValue); + } else { + nsTArray<uint32_t>& samples = gBatch.LookupOrInsert(aName); + samples.AppendElement(aValue); + } + + BatchCheck(lock); +} + +void BoolScalarSet(const nsCString& aName, bool aValue) { + StaticMutexAutoLock lock(gMutex); + + gBoolScalars.InsertOrUpdate(aName, aValue); + + BatchCheck(lock); +} + +void StringScalarSet(const nsCString& aName, const nsCString& aValue) { + StaticMutexAutoLock lock(gMutex); + + gStringScalars.InsertOrUpdate(aName, aValue); + + BatchCheck(lock); +} + +void UintScalarSet(const nsCString& aName, uint32_t aValue) { + StaticMutexAutoLock lock(gMutex); + + gUintScalars.InsertOrUpdate(aName, aValue); + + BatchCheck(lock); +} + +} // namespace GeckoViewStreamingTelemetry diff --git a/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.h b/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.h new file mode 100644 index 0000000000..458224a3c2 --- /dev/null +++ b/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.h @@ -0,0 +1,55 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef GeckoViewStreamingTelemetry_h__ +#define GeckoViewStreamingTelemetry_h__ + +#include "mozilla/Assertions.h" +#include "mozilla/RefPtr.h" +#include "nsISupports.h" +#include "nsStringFwd.h" +#include "nsTArray.h" + +#include <cstdint> + +namespace GeckoViewStreamingTelemetry { + +void HistogramAccumulate(const nsCString& aName, bool aIsCategorical, + uint32_t aValue); + +void BoolScalarSet(const nsCString& aName, bool aValue); +void StringScalarSet(const nsCString& aName, const nsCString& aValue); +void UintScalarSet(const nsCString& aName, uint32_t aValue); + +// Classes wishing to receive Streaming Telemetry must implement this interface +// and register themselves via RegisterDelegate. +class StreamingTelemetryDelegate { + public: + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StreamingTelemetryDelegate) + + // Receive* methods will be called from time to time on the main thread. + virtual void ReceiveHistogramSamples(const nsCString& aName, + const nsTArray<uint32_t>& aSamples) = 0; + virtual void ReceiveCategoricalHistogramSamples( + const nsCString& aName, const nsTArray<uint32_t>& aSamples) = 0; + virtual void ReceiveBoolScalarValue(const nsCString& aName, bool aValue) = 0; + virtual void ReceiveStringScalarValue(const nsCString& aName, + const nsCString& aValue) = 0; + virtual void ReceiveUintScalarValue(const nsCString& aName, + uint32_t aValue) = 0; + + protected: + virtual ~StreamingTelemetryDelegate() = default; +}; + +// Registers the provided StreamingTelemetryDelegate to receive Streaming +// Telemetry, overwriting any previous delegate registration. +// Call on any thread. +void RegisterDelegate(const RefPtr<StreamingTelemetryDelegate>& aDelegate); + +} // namespace GeckoViewStreamingTelemetry + +#endif // GeckoViewStreamingTelemetry_h__ diff --git a/toolkit/components/telemetry/geckoview/streaming/metrics.yaml b/toolkit/components/telemetry/geckoview/streaming/metrics.yaml new file mode 100644 index 0000000000..2386fb2171 --- /dev/null +++ b/toolkit/components/telemetry/geckoview/streaming/metrics.yaml @@ -0,0 +1,13 @@ +# 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 +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# This file defines the metrics that are recorded by the Glean SDK. They are +# automatically converted to platform-specific code at build time using the +# `glean_parser` PyPI package. + +# Adding a new metric? Please don't! +# (At least not without the permission of a Telemetry Module Peer) + +--- +$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0 |