diff options
Diffstat (limited to 'toolkit/components/telemetry/core')
7 files changed, 17 insertions, 1471 deletions
diff --git a/toolkit/components/telemetry/core/Telemetry.cpp b/toolkit/components/telemetry/core/Telemetry.cpp index a0effb02bb..3214210c39 100644 --- a/toolkit/components/telemetry/core/Telemetry.cpp +++ b/toolkit/components/telemetry/core/Telemetry.cpp @@ -16,9 +16,6 @@ #endif #include "base/pickle.h" #include "base/process_util.h" -#if defined(MOZ_TELEMETRY_GECKOVIEW) -# include "geckoview/TelemetryGeckoViewPersistence.h" -#endif #include "ipc/TelemetryIPCAccumulator.h" #include "jsapi.h" #include "jsfriendapi.h" diff --git a/toolkit/components/telemetry/core/TelemetryCommon.cpp b/toolkit/components/telemetry/core/TelemetryCommon.cpp index 7113a682c9..278763eaff 100644 --- a/toolkit/components/telemetry/core/TelemetryCommon.cpp +++ b/toolkit/components/telemetry/core/TelemetryCommon.cpp @@ -194,11 +194,7 @@ JSString* ToJSString(JSContext* cx, const nsAString& aStr) { SupportedProduct GetCurrentProduct() { #if defined(MOZ_WIDGET_ANDROID) - if (mozilla::StaticPrefs::toolkit_telemetry_geckoview_streaming()) { - return SupportedProduct::GeckoviewStreaming; - } else { - return SupportedProduct::Fennec; - } + return SupportedProduct::Fennec; #elif defined(MOZ_THUNDERBIRD) return SupportedProduct::Thunderbird; #else diff --git a/toolkit/components/telemetry/core/TelemetryCommon.h b/toolkit/components/telemetry/core/TelemetryCommon.h index 141410f150..47feef94d2 100644 --- a/toolkit/components/telemetry/core/TelemetryCommon.h +++ b/toolkit/components/telemetry/core/TelemetryCommon.h @@ -42,10 +42,8 @@ static_assert(static_cast<uint16_t>(RecordedProcessType::Main) == 1, enum class SupportedProduct : uint8_t { Firefox = (1 << 0), Fennec = (1 << 1), - // Note that `1 << 2` (former GeckoView) is missing in the representation - // but isn't necessary to be maintained, but we see no point in filling it - // at this time. - GeckoviewStreaming = (1 << 3), + // Note that `1 << 2` and `1 << 3` (former GeckoView, GeckoviewStreaming) are + // missing in the representation. We see no point in filling it at this time. Thunderbird = (1 << 4), }; MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(SupportedProduct); diff --git a/toolkit/components/telemetry/core/TelemetryHistogram.cpp b/toolkit/components/telemetry/core/TelemetryHistogram.cpp index 88ac88eb9e..0ba7009d51 100644 --- a/toolkit/components/telemetry/core/TelemetryHistogram.cpp +++ b/toolkit/components/telemetry/core/TelemetryHistogram.cpp @@ -8,7 +8,6 @@ #include <limits> #include "base/histogram.h" -#include "geckoview/streaming/GeckoViewStreamingTelemetry.h" #include "ipc/TelemetryIPCAccumulator.h" #include "jsapi.h" #include "jsfriendapi.h" @@ -700,15 +699,6 @@ nsresult internal_HistogramAdd(const StaticMutexAutoLock& aLock, return NS_OK; } - if (&histogram != gExpiredHistogram && - GetCurrentProduct() == SupportedProduct::GeckoviewStreaming) { - const HistogramInfo& info = gHistogramInfos[id]; - GeckoViewStreamingTelemetry::HistogramAccumulate( - nsDependentCString(info.name()), - info.histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL, value); - return NS_OK; - } - // The internal representation of a base::Histogram's buckets uses `int`. // Clamp large values of `value` to be INT_MAX so they continue to be treated // as large values (instead of negative ones). @@ -3038,640 +3028,3 @@ size_t TelemetryHistogram::GetHistogramSizesOfIncludingThis( return n; } - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -// -// PRIVATE: GeckoView specific helpers - -namespace base { -class PersistedSampleSet : public base::Histogram::SampleSet { - public: - explicit PersistedSampleSet(const nsTArray<base::Histogram::Count>& aCounts, - int64_t aSampleSum); -}; - -PersistedSampleSet::PersistedSampleSet( - const nsTArray<base::Histogram::Count>& aCounts, int64_t aSampleSum) { - // Initialize the data in the base class. See Histogram::SampleSet - // for the fields documentation. - const size_t numCounts = aCounts.Length(); - counts_.SetLength(numCounts); - - for (size_t i = 0; i < numCounts; i++) { - counts_[i] = aCounts[i]; - redundant_count_ += aCounts[i]; - } - sum_ = aSampleSum; -}; -} // namespace base - -namespace { -/** - * Helper function to write histogram properties to JSON. - * Please note that this needs to be called between - * StartObjectProperty/EndObject calls that mark the histogram's - * JSON creation. - */ -void internal_ReflectHistogramToJSON(const HistogramSnapshotData& aSnapshot, - mozilla::JSONWriter& aWriter) { - aWriter.IntProperty("sum", aSnapshot.mSampleSum); - - // Fill the "counts" property. - aWriter.StartArrayProperty("counts"); - for (size_t i = 0; i < aSnapshot.mBucketCounts.Length(); i++) { - aWriter.IntElement(aSnapshot.mBucketCounts[i]); - } - aWriter.EndArray(); -} - -bool internal_CanRecordHistogram(const HistogramID id, ProcessID aProcessType) { - // Check if we are allowed to record the data. - if (!CanRecordDataset(gHistogramInfos[id].dataset, internal_CanRecordBase(), - internal_CanRecordExtended())) { - return false; - } - - // Check if we're allowed to record in the given process. - if (aProcessType == ProcessID::Parent && !internal_IsRecordingEnabled(id)) { - return false; - } - - if (aProcessType != ProcessID::Parent && - !CanRecordInProcess(gHistogramInfos[id].record_in_processes, - aProcessType)) { - return false; - } - - // Don't record if the current platform is not enabled - if (!CanRecordProduct(gHistogramInfos[id].products)) { - return false; - } - - return true; -} - -nsresult internal_ParseHistogramData( - JSContext* aCx, JS::Handle<JS::PropertyKey> aEntryId, - JS::Handle<JSObject*> aContainerObj, nsACString& aOutName, - nsTArray<base::Histogram::Count>& aOutCountArray, int64_t& aOutSum) { - // Get the histogram name. - nsAutoJSString histogramName; - if (!histogramName.init(aCx, aEntryId)) { - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - CopyUTF16toUTF8(histogramName, aOutName); - - // Get the data for this histogram. - JS::Rooted<JS::Value> histogramData(aCx); - if (!JS_GetPropertyById(aCx, aContainerObj, aEntryId, &histogramData)) { - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - if (!histogramData.isObject()) { - // base::Histogram data need to be an object. If that's not the case, skip - // it and try to load the rest of the data. - return NS_ERROR_FAILURE; - } - - // Get the "sum" property. - JS::Rooted<JS::Value> sumValue(aCx); - JS::Rooted<JSObject*> histogramObj(aCx, &histogramData.toObject()); - if (!JS_GetProperty(aCx, histogramObj, "sum", &sumValue)) { - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - if (!JS::ToInt64(aCx, sumValue, &aOutSum)) { - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - // Get the "counts" array. - JS::Rooted<JS::Value> countsArray(aCx); - bool countsIsArray = false; - if (!JS_GetProperty(aCx, histogramObj, "counts", &countsArray) || - !JS::IsArrayObject(aCx, countsArray, &countsIsArray)) { - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - if (!countsIsArray) { - // The "counts" property needs to be an array. If this is not the case, - // skip this histogram. - return NS_ERROR_FAILURE; - } - - // Get the length of the array. - uint32_t countsLen = 0; - JS::Rooted<JSObject*> countsArrayObj(aCx, &countsArray.toObject()); - if (!JS::GetArrayLength(aCx, countsArrayObj, &countsLen)) { - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - // Parse the "counts" in the array. - for (uint32_t arrayIdx = 0; arrayIdx < countsLen; arrayIdx++) { - JS::Rooted<JS::Value> elementValue(aCx); - int countAsInt = 0; - if (!JS_GetElement(aCx, countsArrayObj, arrayIdx, &elementValue) || - !JS::ToInt32(aCx, elementValue, &countAsInt)) { - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - aOutCountArray.AppendElement(countAsInt); - } - - return NS_OK; -} - -} // Anonymous namespace - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -// -// PUBLIC: GeckoView serialization/deserialization functions. - -nsresult TelemetryHistogram::SerializeHistograms(mozilla::JSONWriter& aWriter) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only save histograms in the parent process"); - if (!XRE_IsParentProcess()) { - return NS_ERROR_FAILURE; - } - - // Include the GPU process in histogram snapshots only if we actually tried - // to launch a process for it. - bool includeGPUProcess = internal_AttemptedGPUProcess(); - - // Take a snapshot of the histograms. - HistogramProcessSnapshotsArray processHistArray; - { - StaticMutexAutoLock locker(gTelemetryHistogramMutex); - // We always request the "opt-in"/"prerelease" dataset: we internally - // record the right subset, so this will only return "prerelease" if - // it was recorded. - if (NS_FAILED(internal_GetHistogramsSnapshot( - locker, "main"_ns, nsITelemetry::DATASET_PRERELEASE_CHANNELS, - false /* aClearSubsession */, includeGPUProcess, - false /* aFilterTest */, processHistArray))) { - return NS_ERROR_FAILURE; - } - } - - // Make the JSON calls on the stashed histograms for every process - for (uint32_t process = 0; process < processHistArray.length(); ++process) { - aWriter.StartObjectProperty( - mozilla::MakeStringSpan(GetNameForProcessID(ProcessID(process)))); - - for (const HistogramSnapshotInfo& hData : processHistArray[process]) { - HistogramID id = hData.histogramID; - - aWriter.StartObjectProperty( - mozilla::MakeStringSpan(gHistogramInfos[id].name())); - internal_ReflectHistogramToJSON(hData.data, aWriter); - aWriter.EndObject(); - } - aWriter.EndObject(); - } - - return NS_OK; -} - -nsresult TelemetryHistogram::SerializeKeyedHistograms( - mozilla::JSONWriter& aWriter) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only save keyed histograms in the parent process"); - if (!XRE_IsParentProcess()) { - return NS_ERROR_FAILURE; - } - - // Include the GPU process in histogram snapshots only if we actually tried - // to launch a process for it. - bool includeGPUProcess = internal_AttemptedGPUProcess(); - - // Take a snapshot of the keyed histograms. - KeyedHistogramProcessSnapshotsArray processHistArray; - { - StaticMutexAutoLock locker(gTelemetryHistogramMutex); - // We always request the "opt-in"/"prerelease" dataset: we internally - // record the right subset, so this will only return "prerelease" if - // it was recorded. - if (NS_FAILED(internal_GetKeyedHistogramsSnapshot( - locker, "main"_ns, nsITelemetry::DATASET_PRERELEASE_CHANNELS, - false /* aClearSubsession */, includeGPUProcess, - false /* aFilterTest */, processHistArray))) { - return NS_ERROR_FAILURE; - } - } - - // Serialize the keyed histograms for every process. - for (uint32_t process = 0; process < processHistArray.length(); ++process) { - aWriter.StartObjectProperty( - mozilla::MakeStringSpan(GetNameForProcessID(ProcessID(process)))); - - const KeyedHistogramSnapshotsArray& hArray = processHistArray[process]; - for (size_t i = 0; i < hArray.length(); ++i) { - const KeyedHistogramSnapshotInfo& hData = hArray[i]; - HistogramID id = hData.histogramId; - const HistogramInfo& info = gHistogramInfos[id]; - - aWriter.StartObjectProperty(mozilla::MakeStringSpan(info.name())); - - // Each key is a new object with a "sum" and a "counts" property. - for (const auto& entry : hData.data) { - const HistogramSnapshotData& keyData = entry.GetData(); - aWriter.StartObjectProperty(PromiseFlatCString(entry.GetKey())); - internal_ReflectHistogramToJSON(keyData, aWriter); - aWriter.EndObject(); - } - - aWriter.EndObject(); - } - aWriter.EndObject(); - } - - return NS_OK; -} - -nsresult TelemetryHistogram::DeserializeHistograms( - JSContext* aCx, JS::Handle<JS::Value> aData) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only load histograms in the parent process"); - if (!XRE_IsParentProcess()) { - return NS_ERROR_FAILURE; - } - - // Telemetry is disabled. This should never happen, but let's leave this check - // for consistency with other histogram updates routines. - if (!internal_CanRecordBase()) { - return NS_OK; - } - - typedef std::tuple<nsCString, nsTArray<base::Histogram::Count>, int64_t> - PersistedHistogramTuple; - typedef mozilla::Vector<PersistedHistogramTuple> PersistedHistogramArray; - typedef mozilla::Vector<PersistedHistogramArray> PersistedHistogramStorage; - - // Before updating the histograms, we need to get the data out of the JS - // wrappers. We can't hold the histogram mutex while handling JS stuff. - // Build a <histogram name, value> map. - JS::Rooted<JSObject*> histogramDataObj(aCx, &aData.toObject()); - JS::Rooted<JS::IdVector> processes(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, histogramDataObj, &processes)) { - // We can't even enumerate the processes in the loaded data, so - // there is nothing we could recover from the persistence file. Bail out. - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - // Make sure we have enough storage for all the processes. - PersistedHistogramStorage histogramsToUpdate; - if (!histogramsToUpdate.resize(static_cast<uint32_t>(ProcessID::Count))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - // The following block of code attempts to extract as much data as possible - // from the serialized JSON, even in case of light data corruptions: if, for - // example, the data for a single process is corrupted or is in an unexpected - // form, we press on and attempt to load the data for the other processes. - JS::Rooted<JS::PropertyKey> process(aCx); - for (auto& processVal : processes) { - // This is required as JS API calls require an Handle<jsid> and not a - // plain jsid. - process = processVal; - // Get the process name. - nsAutoJSString processNameJS; - if (!processNameJS.init(aCx, process)) { - JS_ClearPendingException(aCx); - continue; - } - - // Make sure it's valid. Note that this is safe to call outside - // of a locked section. - NS_ConvertUTF16toUTF8 processName(processNameJS); - ProcessID processID = GetIDForProcessName(processName.get()); - if (processID == ProcessID::Count) { - NS_WARNING( - nsPrintfCString("Failed to get process ID for %s", processName.get()) - .get()); - continue; - } - - // And its probes. - JS::Rooted<JS::Value> processData(aCx); - if (!JS_GetPropertyById(aCx, histogramDataObj, process, &processData)) { - JS_ClearPendingException(aCx); - continue; - } - - if (!processData.isObject()) { - // |processData| should be an object containing histograms. If this is - // not the case, silently skip and try to load the data for the other - // processes. - continue; - } - - // Iterate through each histogram. - JS::Rooted<JSObject*> processDataObj(aCx, &processData.toObject()); - JS::Rooted<JS::IdVector> histograms(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, processDataObj, &histograms)) { - JS_ClearPendingException(aCx); - continue; - } - - // Get a reference to the deserialized data for this process. - PersistedHistogramArray& deserializedProcessData = - histogramsToUpdate[static_cast<uint32_t>(processID)]; - - JS::Rooted<JS::PropertyKey> histogram(aCx); - for (auto& histogramVal : histograms) { - histogram = histogramVal; - - int64_t sum = 0; - nsTArray<base::Histogram::Count> deserializedCounts; - nsCString histogramName; - if (NS_FAILED(internal_ParseHistogramData(aCx, histogram, processDataObj, - histogramName, - deserializedCounts, sum))) { - continue; - } - - // Finally append the deserialized data to the storage. - if (!deserializedProcessData.emplaceBack(std::make_tuple( - std::move(histogramName), std::move(deserializedCounts), sum))) { - return NS_ERROR_OUT_OF_MEMORY; - } - } - } - - // Update the histogram storage. - { - StaticMutexAutoLock locker(gTelemetryHistogramMutex); - - for (uint32_t process = 0; process < histogramsToUpdate.length(); - ++process) { - PersistedHistogramArray& processArray = histogramsToUpdate[process]; - - for (auto& histogramData : processArray) { - // Attempt to get the corresponding ID for the deserialized histogram - // name. - HistogramID id; - if (NS_FAILED(internal_GetHistogramIdByName( - locker, std::get<0>(histogramData), &id))) { - continue; - } - - ProcessID procID = static_cast<ProcessID>(process); - if (!internal_CanRecordHistogram(id, procID)) { - // We're not allowed to record this, so don't try to restore it. - continue; - } - - // Get the Histogram instance: this will instantiate it if it doesn't - // exist. - Histogram* w = internal_GetHistogramById(locker, id, procID); - MOZ_ASSERT(w); - - if (!w || w->IsExpired()) { - continue; - } - - base::Histogram* h = nullptr; - constexpr auto store = "main"_ns; - if (!w->GetHistogram(store, &h)) { - continue; - } - MOZ_ASSERT(h); - - if (!h) { - // Don't restore expired histograms. - continue; - } - - // Make sure that histogram counts have matching sizes. If not, - // |AddSampleSet| will fail and crash. - size_t numCounts = std::get<1>(histogramData).Length(); - if (h->bucket_count() != numCounts) { - MOZ_ASSERT(false, - "The number of restored buckets does not match with the " - "on in the definition"); - continue; - } - - // Update the data for the histogram. - h->AddSampleSet(base::PersistedSampleSet( - std::move(std::get<1>(histogramData)), std::get<2>(histogramData))); - } - } - } - - return NS_OK; -} - -nsresult TelemetryHistogram::DeserializeKeyedHistograms( - JSContext* aCx, JS::Handle<JS::Value> aData) { - MOZ_ASSERT(XRE_IsParentProcess(), - "Only load keyed histograms in the parent process"); - if (!XRE_IsParentProcess()) { - return NS_ERROR_FAILURE; - } - - // Telemetry is disabled. This should never happen, but let's leave this check - // for consistency with other histogram updates routines. - if (!internal_CanRecordBase()) { - return NS_OK; - } - - typedef std::tuple<nsCString, nsCString, nsTArray<base::Histogram::Count>, - int64_t> - PersistedKeyedHistogramTuple; - typedef mozilla::Vector<PersistedKeyedHistogramTuple> - PersistedKeyedHistogramArray; - typedef mozilla::Vector<PersistedKeyedHistogramArray> - PersistedKeyedHistogramStorage; - - // Before updating the histograms, we need to get the data out of the JS - // wrappers. We can't hold the histogram mutex while handling JS stuff. - // Build a <histogram name, value> map. - JS::Rooted<JSObject*> histogramDataObj(aCx, &aData.toObject()); - JS::Rooted<JS::IdVector> processes(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, histogramDataObj, &processes)) { - // We can't even enumerate the processes in the loaded data, so - // there is nothing we could recover from the persistence file. Bail out. - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - // Make sure we have enough storage for all the processes. - PersistedKeyedHistogramStorage histogramsToUpdate; - if (!histogramsToUpdate.resize(static_cast<uint32_t>(ProcessID::Count))) { - return NS_ERROR_OUT_OF_MEMORY; - } - - // The following block of code attempts to extract as much data as possible - // from the serialized JSON, even in case of light data corruptions: if, for - // example, the data for a single process is corrupted or is in an unexpected - // form, we press on and attempt to load the data for the other processes. - JS::Rooted<JS::PropertyKey> process(aCx); - for (auto& processVal : processes) { - // This is required as JS API calls require an Handle<jsid> and not a - // plain jsid. - process = processVal; - // Get the process name. - nsAutoJSString processNameJS; - if (!processNameJS.init(aCx, process)) { - JS_ClearPendingException(aCx); - continue; - } - - // Make sure it's valid. Note that this is safe to call outside - // of a locked section. - NS_ConvertUTF16toUTF8 processName(processNameJS); - ProcessID processID = GetIDForProcessName(processName.get()); - if (processID == ProcessID::Count) { - NS_WARNING( - nsPrintfCString("Failed to get process ID for %s", processName.get()) - .get()); - continue; - } - - // And its probes. - JS::Rooted<JS::Value> processData(aCx); - if (!JS_GetPropertyById(aCx, histogramDataObj, process, &processData)) { - JS_ClearPendingException(aCx); - continue; - } - - if (!processData.isObject()) { - // |processData| should be an object containing histograms. If this is - // not the case, silently skip and try to load the data for the other - // processes. - continue; - } - - // Iterate through each keyed histogram. - JS::Rooted<JSObject*> processDataObj(aCx, &processData.toObject()); - JS::Rooted<JS::IdVector> histograms(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, processDataObj, &histograms)) { - JS_ClearPendingException(aCx); - continue; - } - - // Get a reference to the deserialized data for this process. - PersistedKeyedHistogramArray& deserializedProcessData = - histogramsToUpdate[static_cast<uint32_t>(processID)]; - - JS::Rooted<JS::PropertyKey> histogram(aCx); - for (auto& histogramVal : histograms) { - histogram = histogramVal; - // Get the histogram name. - nsAutoJSString histogramName; - if (!histogramName.init(aCx, histogram)) { - JS_ClearPendingException(aCx); - continue; - } - - // Get the data for this histogram. - JS::Rooted<JS::Value> histogramData(aCx); - if (!JS_GetPropertyById(aCx, processDataObj, histogram, &histogramData)) { - JS_ClearPendingException(aCx); - continue; - } - - // Iterate through each key in the histogram. - JS::Rooted<JSObject*> keysDataObj(aCx, &histogramData.toObject()); - JS::Rooted<JS::IdVector> keys(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, keysDataObj, &keys)) { - JS_ClearPendingException(aCx); - continue; - } - - JS::Rooted<JS::PropertyKey> key(aCx); - for (auto& keyVal : keys) { - key = keyVal; - - int64_t sum = 0; - nsTArray<base::Histogram::Count> deserializedCounts; - nsCString keyName; - if (NS_FAILED(internal_ParseHistogramData( - aCx, key, keysDataObj, keyName, deserializedCounts, sum))) { - continue; - } - - // Finally append the deserialized data to the storage. - if (!deserializedProcessData.emplaceBack(std::make_tuple( - nsCString(NS_ConvertUTF16toUTF8(histogramName)), - std::move(keyName), std::move(deserializedCounts), sum))) { - return NS_ERROR_OUT_OF_MEMORY; - } - } - } - } - - // Update the keyed histogram storage. - { - StaticMutexAutoLock locker(gTelemetryHistogramMutex); - - for (uint32_t process = 0; process < histogramsToUpdate.length(); - ++process) { - PersistedKeyedHistogramArray& processArray = histogramsToUpdate[process]; - - for (auto& histogramData : processArray) { - // Attempt to get the corresponding ID for the deserialized histogram - // name. - HistogramID id; - if (NS_FAILED(internal_GetHistogramIdByName( - locker, std::get<0>(histogramData), &id))) { - continue; - } - - ProcessID procID = static_cast<ProcessID>(process); - if (!internal_CanRecordHistogram(id, procID)) { - // We're not allowed to record this, so don't try to restore it. - continue; - } - - KeyedHistogram* keyed = internal_GetKeyedHistogramById(id, procID); - MOZ_ASSERT(keyed); - - if (!keyed || keyed->IsExpired()) { - // Don't restore if we don't have a destination storage or the - // histogram is expired. - continue; - } - - // Get data for the key we're looking for. - base::Histogram* h = nullptr; - if (NS_FAILED(keyed->GetHistogram("main"_ns, std::get<1>(histogramData), - &h))) { - continue; - } - MOZ_ASSERT(h); - - if (!h) { - // Don't restore if we don't have a destination storage. - continue; - } - - // Make sure that histogram counts have matching sizes. If not, - // |AddSampleSet| will fail and crash. - size_t numCounts = std::get<2>(histogramData).Length(); - if (h->bucket_count() != numCounts) { - MOZ_ASSERT(false, - "The number of restored buckets does not match with the " - "on in the definition"); - continue; - } - - // Update the data for the histogram. - h->AddSampleSet(base::PersistedSampleSet( - std::move(std::get<2>(histogramData)), std::get<3>(histogramData))); - } - } - } - - return NS_OK; -} diff --git a/toolkit/components/telemetry/core/TelemetryHistogram.h b/toolkit/components/telemetry/core/TelemetryHistogram.h index 9f415f3637..f5aaa60634 100644 --- a/toolkit/components/telemetry/core/TelemetryHistogram.h +++ b/toolkit/components/telemetry/core/TelemetryHistogram.h @@ -12,11 +12,6 @@ #include "nsXULAppAPI.h" #include "TelemetryCommon.h" -namespace mozilla { -// This is only used for the GeckoView persistence. -class JSONWriter; -} // namespace mozilla - // 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. @@ -110,15 +105,6 @@ nsresult GetKeyedHistogramSnapshots(JSContext* aCx, size_t GetHistogramSizesOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); -// These functions are only meant to be used for GeckoView persistence. -// They are responsible for updating in-memory probes with the data persisted -// on the disk and vice-versa. -nsresult SerializeHistograms(mozilla::JSONWriter& aWriter); -nsresult SerializeKeyedHistograms(mozilla::JSONWriter& aWriter); -nsresult DeserializeHistograms(JSContext* aCx, JS::Handle<JS::Value> aData); -nsresult DeserializeKeyedHistograms(JSContext* aCx, - JS::Handle<JS::Value> aData); - } // namespace TelemetryHistogram #endif // TelemetryHistogram_h__ diff --git a/toolkit/components/telemetry/core/TelemetryScalar.cpp b/toolkit/components/telemetry/core/TelemetryScalar.cpp index 8a121e8f3f..312e25ddc4 100644 --- a/toolkit/components/telemetry/core/TelemetryScalar.cpp +++ b/toolkit/components/telemetry/core/TelemetryScalar.cpp @@ -6,7 +6,6 @@ #include "TelemetryScalar.h" -#include "geckoview/streaming/GeckoViewStreamingTelemetry.h" #include "ipc/TelemetryIPCAccumulator.h" #include "js/Array.h" // JS::GetArrayLength, JS::IsArrayObject #include "js/PropertyAndElement.h" // JS_DefineProperty, JS_DefineUCProperty, JS_Enumerate, JS_GetElement, JS_GetProperty, JS_GetPropertyById, JS_HasProperty @@ -27,6 +26,7 @@ #include "nsJSUtils.h" #include "nsPrintfCString.h" #include "nsVariant.h" +#include "TelemetryCommon.h" #include "TelemetryScalarData.h" using mozilla::MakeUnique; @@ -112,11 +112,6 @@ const uint32_t kMaximumScalarNameLength = 40; const uint32_t kScalarCount = static_cast<uint32_t>(mozilla::Telemetry::ScalarID::ScalarCount); -// To stop growing unbounded in memory while waiting for scalar deserialization -// to finish, we immediately apply pending operations if the array reaches -// a certain high water mark of elements. -const size_t kScalarActionsArrayHighWaterMark = 10000; - const char* TEST_SCALAR_PREFIX = "telemetry.test."; // The max offset supported by gScalarStoresTable for static scalars' stores. @@ -284,45 +279,6 @@ ScalarResult GetVariantFromIVariant(nsIVariant* aInput, uint32_t aScalarKind, return ScalarResult::Ok; } -/** - * Write a nsIVariant with a JSONWriter, used for GeckoView persistence. - */ -nsresult WriteVariantToJSONWriter( - uint32_t aScalarType, nsIVariant* aInputValue, - const mozilla::Span<const char>& aPropertyName, - mozilla::JSONWriter& aWriter) { - MOZ_ASSERT(aInputValue); - - switch (aScalarType) { - case nsITelemetry::SCALAR_TYPE_COUNT: { - uint32_t val = 0; - nsresult rv = aInputValue->GetAsUint32(&val); - NS_ENSURE_SUCCESS(rv, rv); - aWriter.IntProperty(aPropertyName, val); - break; - } - case nsITelemetry::SCALAR_TYPE_STRING: { - nsCString val; - nsresult rv = aInputValue->GetAsACString(val); - NS_ENSURE_SUCCESS(rv, rv); - aWriter.StringProperty(aPropertyName, val); - break; - } - case nsITelemetry::SCALAR_TYPE_BOOLEAN: { - bool val = false; - nsresult rv = aInputValue->GetAsBool(&val); - NS_ENSURE_SUCCESS(rv, rv); - aWriter.BoolProperty(aPropertyName, val); - break; - } - default: - MOZ_ASSERT(false, "Unknown scalar kind."); - return NS_ERROR_FAILURE; - } - - return NS_OK; -} - // Implements the methods for ScalarInfo. const char* ScalarInfo::name() const { return &gScalarsStringTable[this->name_offset]; @@ -517,10 +473,6 @@ ScalarResult ScalarUnsigned::SetValue(nsIVariant* aValue) { } void ScalarUnsigned::SetValue(uint32_t aValue) { - if (GetCurrentProduct() == SupportedProduct::GeckoviewStreaming) { - GeckoViewStreamingTelemetry::UintScalarSet(mName, aValue); - return; - } for (auto& val : mStorage) { val = aValue; } @@ -544,7 +496,6 @@ ScalarResult ScalarUnsigned::AddValue(nsIVariant* aValue) { } void ScalarUnsigned::AddValue(uint32_t aValue) { - MOZ_ASSERT(GetCurrentProduct() != SupportedProduct::GeckoviewStreaming); for (auto& val : mStorage) { val += aValue; } @@ -552,7 +503,6 @@ void ScalarUnsigned::AddValue(uint32_t aValue) { } ScalarResult ScalarUnsigned::SetMaximum(nsIVariant* aValue) { - MOZ_ASSERT(GetCurrentProduct() != SupportedProduct::GeckoviewStreaming); ScalarResult sr = CheckInput(aValue); if (sr == ScalarResult::UnsignedNegativeValue) { return sr; @@ -678,13 +628,6 @@ ScalarResult ScalarString::SetValue(nsIVariant* aValue) { ScalarResult ScalarString::SetValue(const nsAString& aValue) { auto str = Substring(aValue, 0, kMaximumStringValueLength); - if (GetCurrentProduct() == SupportedProduct::GeckoviewStreaming) { - GeckoViewStreamingTelemetry::StringScalarSet(mName, - NS_ConvertUTF16toUTF8(str)); - return aValue.Length() > kMaximumStringValueLength - ? ScalarResult::StringTooLong - : ScalarResult::Ok; - } for (auto& val : mStorage) { val.Assign(str); } @@ -779,10 +722,6 @@ ScalarResult ScalarBoolean::SetValue(nsIVariant* aValue) { }; void ScalarBoolean::SetValue(bool aValue) { - if (GetCurrentProduct() == SupportedProduct::GeckoviewStreaming) { - GeckoViewStreamingTelemetry::BoolScalarSet(mName, aValue); - return; - } for (auto& val : mStorage) { val = aValue; } @@ -1208,20 +1147,6 @@ ProcessesKeyedScalarsMapType gKeyedScalarStorageMap; // needed to support "build faster" in local developer builds. ProcessesScalarsMapType gDynamicBuiltinScalarStorageMap; ProcessesKeyedScalarsMapType gDynamicBuiltinKeyedScalarStorageMap; - -// Whether or not the deserialization of persisted scalars is still in progress. -// This is never the case on Desktop or Fennec. -// Only GeckoView restores persisted scalars. -bool gIsDeserializing = false; -// This batches scalar accumulations that should be applied once loading -// finished. -StaticAutoPtr<nsTArray<ScalarAction>> gScalarsActions; -StaticAutoPtr<nsTArray<KeyedScalarAction>> gKeyedScalarsActions; - -bool internal_IsScalarDeserializing(const StaticMutexAutoLock& lock) { - return gIsDeserializing; -} - } // namespace //////////////////////////////////////////////////////////////////////// @@ -1402,8 +1327,6 @@ bool internal_CanRecordForScalarID(const StaticMutexAutoLock& lock, * @param aKeyed Are we attempting to write a keyed scalar? * @param aForce Whether to allow recording even if the probe is not allowed on * the current process. - * This must only be true for GeckoView persistence and recorded - * actions. * @return ScalarResult::Ok if we can record, an error code otherwise. */ ScalarResult internal_CanRecordScalar(const StaticMutexAutoLock& lock, @@ -1546,108 +1469,6 @@ nsresult internal_GetScalarByEnum(const StaticMutexAutoLock& lock, return NS_OK; } -void internal_ApplyPendingOperations(const StaticMutexAutoLock& lock); - -/** - * Record the given action on a scalar into the pending actions list. - * - * If the pending actions list overflows the high water mark length - * all operations are immediately applied, including the passed action. - * - * @param aScalarAction The action to record. - */ -void internal_RecordScalarAction(const StaticMutexAutoLock& lock, - const ScalarAction& aScalarAction) { - // Make sure to have the storage. - if (!gScalarsActions) { - gScalarsActions = new nsTArray<ScalarAction>(); - } - - // Store the action. - gScalarsActions->AppendElement(aScalarAction); - - // If this action overflows the pending actions array, we immediately apply - // pending operations and assume loading is over. If loading still happens - // afterwards, some scalar values might be overwritten and inconsistent, but - // we won't lose operations on otherwise untouched probes. - if (gScalarsActions->Length() > kScalarActionsArrayHighWaterMark) { - internal_ApplyPendingOperations(lock); - return; - } -} - -/** - * Record the given action on a scalar on the main process into the pending - * actions list. - * - * If the pending actions list overflows the high water mark length - * all operations are immediately applied, including the passed action. - * - * @param aId The scalar's ID this action applies to - * @param aDynamic Determines if the scalar is dynamic - * @param aAction The action to record - * @param aValue The additional data for the recorded action - */ -void internal_RecordScalarAction(const StaticMutexAutoLock& lock, uint32_t aId, - bool aDynamic, ScalarActionType aAction, - const ScalarVariant& aValue) { - internal_RecordScalarAction( - lock, - ScalarAction{aId, aDynamic, aAction, Some(aValue), ProcessID::Parent}); -} - -/** - * Record the given action on a keyed scalar into the pending actions list. - * - * If the pending actions list overflows the high water mark length - * all operations are immediately applied, including the passed action. - * - * @param aScalarAction The action to record. - */ -void internal_RecordKeyedScalarAction(const StaticMutexAutoLock& lock, - const KeyedScalarAction& aScalarAction) { - // Make sure to have the storage. - if (!gKeyedScalarsActions) { - gKeyedScalarsActions = new nsTArray<KeyedScalarAction>(); - } - - // Store the action. - gKeyedScalarsActions->AppendElement(aScalarAction); - - // If this action overflows the pending actions array, we immediately apply - // pending operations and assume loading is over. If loading still happens - // afterwards, some scalar values might be overwritten and inconsistent, but - // we won't lose operations on otherwise untouched probes. - if (gKeyedScalarsActions->Length() > kScalarActionsArrayHighWaterMark) { - internal_ApplyPendingOperations(lock); - return; - } -} - -/** - * Record the given action on a keyed scalar on the main process into the - * pending actions list. - * - * If the pending actions list overflows the high water mark length - * all operations are immediately applied, including the passed action. - * - * @param aId The scalar's ID this action applies to - * @param aDynamic Determines if the scalar is dynamic - * @param aKey The scalar's key - * @param aAction The action to record - * @param aValue The additional data for the recorded action - */ -void internal_RecordKeyedScalarAction(const StaticMutexAutoLock& lock, - uint32_t aId, bool aDynamic, - const nsAString& aKey, - ScalarActionType aAction, - const ScalarVariant& aValue) { - internal_RecordKeyedScalarAction( - lock, - KeyedScalarAction{aId, aDynamic, aAction, NS_ConvertUTF16toUTF8(aKey), - Some(aValue), ProcessID::Parent}); -} - /** * Update the scalar with the provided value. This is used by the JS API. * @@ -1655,16 +1476,11 @@ void internal_RecordKeyedScalarAction(const StaticMutexAutoLock& lock, * @param aName The scalar name. * @param aType The action type for updating the scalar. * @param aValue The value to use for updating the scalar. - * @param aProcessOverride The process for which the scalar must be updated. - * This must only be used for GeckoView persistence. It must be - * set to the ProcessID::Parent for all the other cases. - * @param aForce Whether to force updating even if load is in progress. * @return a ScalarResult error value. */ -ScalarResult internal_UpdateScalar( - const StaticMutexAutoLock& lock, const nsACString& aName, - ScalarActionType aType, nsIVariant* aValue, - ProcessID aProcessOverride = ProcessID::Parent, bool aForce = false) { +ScalarResult internal_UpdateScalar(const StaticMutexAutoLock& lock, + const nsACString& aName, + ScalarActionType aType, nsIVariant* aValue) { ScalarKey uniqueId; nsresult rv = internal_GetEnumByScalarName(lock, aName, &uniqueId); if (NS_FAILED(rv)) { @@ -1672,7 +1488,7 @@ ScalarResult internal_UpdateScalar( : ScalarResult::UnknownScalar; } - ScalarResult sr = internal_CanRecordScalar(lock, uniqueId, false, aForce); + ScalarResult sr = internal_CanRecordScalar(lock, uniqueId, false, false); if (sr != ScalarResult::Ok) { if (sr == ScalarResult::CannotRecordDataset) { return ScalarResult::Ok; @@ -1695,23 +1511,9 @@ ScalarResult internal_UpdateScalar( return ScalarResult::Ok; } - if (!aForce && internal_IsScalarDeserializing(lock)) { - const BaseScalarInfo& info = internal_GetScalarInfo(lock, uniqueId); - // Convert the nsIVariant to a Variant. - mozilla::Maybe<ScalarVariant> variantValue; - sr = GetVariantFromIVariant(aValue, info.kind, variantValue); - if (sr != ScalarResult::Ok) { - MOZ_ASSERT(false, "Unable to convert nsIVariant to mozilla::Variant."); - return sr; - } - internal_RecordScalarAction(lock, uniqueId.id, uniqueId.dynamic, aType, - variantValue.ref()); - return ScalarResult::Ok; - } - // Finally get the scalar. ScalarBase* scalar = nullptr; - rv = internal_GetScalarByEnum(lock, uniqueId, aProcessOverride, &scalar); + rv = internal_GetScalarByEnum(lock, uniqueId, ProcessID::Parent, &scalar); if (NS_FAILED(rv)) { // Don't throw on expired scalars. if (rv == NS_ERROR_NOT_AVAILABLE) { @@ -1823,15 +1625,13 @@ nsresult internal_GetKeyedScalarByEnum(const StaticMutexAutoLock& lock, * @param aKey The key name. * @param aType The action type for updating the scalar. * @param aValue The value to use for updating the scalar. - * @param aProcessOverride The process for which the scalar must be updated. - * This must only be used for GeckoView persistence. It must be - * set to the ProcessID::Parent for all the other cases. * @return a ScalarResult error value. */ -ScalarResult internal_UpdateKeyedScalar( - const StaticMutexAutoLock& lock, const nsACString& aName, - const nsAString& aKey, ScalarActionType aType, nsIVariant* aValue, - ProcessID aProcessOverride = ProcessID::Parent, bool aForce = false) { +ScalarResult internal_UpdateKeyedScalar(const StaticMutexAutoLock& lock, + const nsACString& aName, + const nsAString& aKey, + ScalarActionType aType, + nsIVariant* aValue) { ScalarKey uniqueId; nsresult rv = internal_GetEnumByScalarName(lock, aName, &uniqueId); if (NS_FAILED(rv)) { @@ -1839,7 +1639,7 @@ ScalarResult internal_UpdateKeyedScalar( : ScalarResult::UnknownScalar; } - ScalarResult sr = internal_CanRecordScalar(lock, uniqueId, true, aForce); + ScalarResult sr = internal_CanRecordScalar(lock, uniqueId, true, false); if (sr != ScalarResult::Ok) { if (sr == ScalarResult::CannotRecordDataset) { return ScalarResult::Ok; @@ -1862,23 +1662,10 @@ ScalarResult internal_UpdateKeyedScalar( return ScalarResult::Ok; } - if (!aForce && internal_IsScalarDeserializing(lock)) { - const BaseScalarInfo& info = internal_GetScalarInfo(lock, uniqueId); - // Convert the nsIVariant to a Variant. - mozilla::Maybe<ScalarVariant> variantValue; - sr = GetVariantFromIVariant(aValue, info.kind, variantValue); - if (sr != ScalarResult::Ok) { - MOZ_ASSERT(false, "Unable to convert nsIVariant to mozilla::Variant."); - return sr; - } - internal_RecordKeyedScalarAction(lock, uniqueId.id, uniqueId.dynamic, aKey, - aType, variantValue.ref()); - return ScalarResult::Ok; - } - // Finally get the scalar. KeyedScalar* scalar = nullptr; - rv = internal_GetKeyedScalarByEnum(lock, uniqueId, aProcessOverride, &scalar); + rv = + internal_GetKeyedScalarByEnum(lock, uniqueId, ProcessID::Parent, &scalar); if (NS_FAILED(rv)) { // Don't throw on expired scalars. if (rv == NS_ERROR_NOT_AVAILABLE) { @@ -2392,21 +2179,6 @@ void internal_ApplyKeyedScalarActions( } } -void internal_ApplyPendingOperations(const StaticMutexAutoLock& lock) { - if (gScalarsActions && gScalarsActions->Length() > 0) { - internal_ApplyScalarActions(lock, *gScalarsActions); - gScalarsActions->Clear(); - } - - if (gKeyedScalarsActions && gKeyedScalarsActions->Length() > 0) { - internal_ApplyKeyedScalarActions(lock, *gKeyedScalarsActions); - gKeyedScalarsActions->Clear(); - } - - // After all pending operations are applied deserialization is done - gIsDeserializing = false; -} - } // namespace //////////////////////////////////////////////////////////////////////// @@ -2477,16 +2249,6 @@ void TelemetryScalar::DeInitializeGlobalState() { gInitDone = false; } -void TelemetryScalar::DeserializationStarted() { - StaticMutexAutoLock locker(gTelemetryScalarsMutex); - gIsDeserializing = true; -} - -void TelemetryScalar::ApplyPendingOperations() { - StaticMutexAutoLock locker(gTelemetryScalarsMutex); - internal_ApplyPendingOperations(locker); -} - void TelemetryScalar::SetCanRecordBase(bool b) { StaticMutexAutoLock locker(gTelemetryScalarsMutex); gCanRecordBase = b; @@ -2596,12 +2358,6 @@ void TelemetryScalar::Add(mozilla::Telemetry::ScalarID aId, uint32_t aValue) { return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordScalarAction(locker, uniqueId.id, uniqueId.dynamic, - ScalarActionType::eAdd, ScalarVariant(aValue)); - return; - } - ScalarBase* scalar = nullptr; nsresult rv = internal_GetScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -2642,13 +2398,6 @@ void TelemetryScalar::Add(mozilla::Telemetry::ScalarID aId, return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordKeyedScalarAction(locker, uniqueId.id, uniqueId.dynamic, - aKey, ScalarActionType::eAdd, - ScalarVariant(aValue)); - return; - } - KeyedScalar* scalar = nullptr; nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -2758,12 +2507,6 @@ void TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, uint32_t aValue) { return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordScalarAction(locker, uniqueId.id, uniqueId.dynamic, - ScalarActionType::eSet, ScalarVariant(aValue)); - return; - } - ScalarBase* scalar = nullptr; nsresult rv = internal_GetScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -2803,13 +2546,6 @@ void TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordScalarAction(locker, uniqueId.id, uniqueId.dynamic, - ScalarActionType::eSet, - ScalarVariant(nsString(aValue))); - return; - } - ScalarBase* scalar = nullptr; nsresult rv = internal_GetScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -2848,12 +2584,6 @@ void TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, bool aValue) { return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordScalarAction(locker, uniqueId.id, uniqueId.dynamic, - ScalarActionType::eSet, ScalarVariant(aValue)); - return; - } - ScalarBase* scalar = nullptr; nsresult rv = internal_GetScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -2894,13 +2624,6 @@ void TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordKeyedScalarAction(locker, uniqueId.id, uniqueId.dynamic, - aKey, ScalarActionType::eSet, - ScalarVariant(aValue)); - return; - } - KeyedScalar* scalar = nullptr; nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -2941,13 +2664,6 @@ void TelemetryScalar::Set(mozilla::Telemetry::ScalarID aId, return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordKeyedScalarAction(locker, uniqueId.id, uniqueId.dynamic, - aKey, ScalarActionType::eSet, - ScalarVariant(aValue)); - return; - } - KeyedScalar* scalar = nullptr; nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -3061,13 +2777,6 @@ void TelemetryScalar::SetMaximum(mozilla::Telemetry::ScalarID aId, return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordScalarAction(locker, uniqueId.id, uniqueId.dynamic, - ScalarActionType::eSetMaximum, - ScalarVariant(aValue)); - return; - } - ScalarBase* scalar = nullptr; nsresult rv = internal_GetScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -3108,13 +2817,6 @@ void TelemetryScalar::SetMaximum(mozilla::Telemetry::ScalarID aId, return; } - if (internal_IsScalarDeserializing(locker)) { - internal_RecordKeyedScalarAction(locker, uniqueId.id, uniqueId.dynamic, - aKey, ScalarActionType::eSetMaximum, - ScalarVariant(aValue)); - return; - } - KeyedScalar* scalar = nullptr; nsresult rv = internal_GetKeyedScalarByEnum(locker, uniqueId, ProcessID::Parent, &scalar); @@ -3538,8 +3240,6 @@ void TelemetryScalar::ClearScalars() { gKeyedScalarStorageMap.Clear(); gDynamicBuiltinScalarStorageMap.Clear(); gDynamicBuiltinKeyedScalarStorageMap.Clear(); - gScalarsActions = nullptr; - gKeyedScalarsActions = nullptr; } size_t TelemetryScalar::GetMapShallowSizesOfExcludingThis( @@ -3580,20 +3280,6 @@ void TelemetryScalar::UpdateChildData( "parent process."); StaticMutexAutoLock locker(gTelemetryScalarsMutex); - // If scalars are still being deserialized, we need to record the incoming - // operations as well. - if (internal_IsScalarDeserializing(locker)) { - for (const ScalarAction& action : aScalarActions) { - // We're only getting immutable access, so let's copy it - ScalarAction copy = action; - // Fix up the process type - copy.mProcessType = aProcessType; - internal_RecordScalarAction(locker, copy); - } - - return; - } - internal_ApplyScalarActions(locker, aScalarActions, Some(aProcessType)); } @@ -3605,20 +3291,6 @@ void TelemetryScalar::UpdateChildKeyedData( "from the parent process."); StaticMutexAutoLock locker(gTelemetryScalarsMutex); - // If scalars are still being deserialized, we need to record the incoming - // operations as well. - if (internal_IsScalarDeserializing(locker)) { - for (const KeyedScalarAction& action : aScalarActions) { - // We're only getting immutable access, so let's copy it - KeyedScalarAction copy = action; - // Fix up the process type - copy.mProcessType = aProcessType; - internal_RecordKeyedScalarAction(locker, copy); - } - - return; - } - internal_ApplyKeyedScalarActions(locker, aScalarActions, Some(aProcessType)); } @@ -3632,10 +3304,6 @@ void TelemetryScalar::RecordDiscardedData( return; } - if (GetCurrentProduct() == SupportedProduct::GeckoviewStreaming) { - return; - } - ScalarBase* scalar = nullptr; mozilla::DebugOnly<nsresult> rv; @@ -3754,437 +3422,3 @@ nsresult TelemetryScalar::GetAllStores(StringHashSet& set) { return NS_OK; } - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -// -// PUBLIC: GeckoView serialization/deserialization functions. - -/** - * Write the scalar data to the provided Json object, for - * GeckoView measurement persistence. The output format is the same one used - * for snapshotting the scalars. - * - * @param {aWriter} The JSON object to write to. - * @returns NS_OK or a failure value explaining why persistence failed. - */ -nsresult TelemetryScalar::SerializeScalars(mozilla::JSONWriter& aWriter) { - // Get a copy of the data, without clearing. - ScalarSnapshotTable scalarsToReflect; - { - StaticMutexAutoLock locker(gTelemetryScalarsMutex); - // For persistence, we care about all the datasets. Worst case, they - // will be empty. - nsresult rv = internal_GetScalarSnapshot( - locker, scalarsToReflect, nsITelemetry::DATASET_PRERELEASE_CHANNELS, - false, /*aClearScalars*/ - "main"_ns); - if (NS_FAILED(rv)) { - return rv; - } - } - - // Persist the scalars to the JSON object. - for (const auto& entry : scalarsToReflect) { - const ScalarTupleArray& processScalars = entry.GetData(); - const char* processName = GetNameForProcessID(ProcessID(entry.GetKey())); - - aWriter.StartObjectProperty(mozilla::MakeStringSpan(processName)); - - for (const ScalarDataTuple& scalar : processScalars) { - nsresult rv = WriteVariantToJSONWriter( - std::get<2>(scalar) /*aScalarType*/, - std::get<1>(scalar) /*aInputValue*/, - mozilla::MakeStringSpan(std::get<0>(scalar)) /*aPropertyName*/, - aWriter /*aWriter*/); - if (NS_FAILED(rv)) { - // Skip this scalar if we failed to write it. We don't bail out just - // yet as we may salvage other scalars. We eventually need to call - // EndObject. - continue; - } - } - - aWriter.EndObject(); - } - - return NS_OK; -} - -/** - * Write the keyed scalar data to the provided Json object, for - * GeckoView measurement persistence. The output format is the same - * one used for snapshotting the keyed scalars. - * - * @param {aWriter} The JSON object to write to. - * @returns NS_OK or a failure value explaining why persistence failed. - */ -nsresult TelemetryScalar::SerializeKeyedScalars(mozilla::JSONWriter& aWriter) { - // Get a copy of the data, without clearing. - KeyedScalarSnapshotTable keyedScalarsToReflect; - { - StaticMutexAutoLock locker(gTelemetryScalarsMutex); - // For persistence, we care about all the datasets. Worst case, they - // will be empty. - nsresult rv = internal_GetKeyedScalarSnapshot( - locker, keyedScalarsToReflect, - nsITelemetry::DATASET_PRERELEASE_CHANNELS, false, /*aClearScalars*/ - "main"_ns); - if (NS_FAILED(rv)) { - return rv; - } - } - - // Persist the scalars to the JSON object. - for (const auto& entry : keyedScalarsToReflect) { - const KeyedScalarTupleArray& processScalars = entry.GetData(); - const char* processName = GetNameForProcessID(ProcessID(entry.GetKey())); - - aWriter.StartObjectProperty(mozilla::MakeStringSpan(processName)); - - for (const KeyedScalarDataTuple& keyedScalarData : processScalars) { - aWriter.StartObjectProperty( - mozilla::MakeStringSpan(std::get<0>(keyedScalarData))); - - // Define a property for each scalar key, then add it to the keyed scalar - // object. - const nsTArray<KeyedScalar::KeyValuePair>& keyProps = - std::get<1>(keyedScalarData); - for (const KeyedScalar::KeyValuePair& keyData : keyProps) { - nsresult rv = WriteVariantToJSONWriter( - std::get<2>(keyedScalarData) /*aScalarType*/, - keyData.second /*aInputValue*/, - PromiseFlatCString(keyData.first) /*aOutKey*/, aWriter /*aWriter*/); - if (NS_FAILED(rv)) { - // Skip this scalar if we failed to write it. We don't bail out just - // yet as we may salvage other scalars. We eventually need to call - // EndObject. - continue; - } - } - aWriter.EndObject(); - } - aWriter.EndObject(); - } - - return NS_OK; -} - -/** - * Load the persisted measurements from a Json object and inject them - * in the relevant process storage. - * - * @param {aData} The input Json object. - * @returns NS_OK if loading was performed, an error code explaining the - * failure reason otherwise. - */ -nsresult TelemetryScalar::DeserializePersistedScalars( - JSContext* aCx, JS::Handle<JS::Value> aData) { - MOZ_ASSERT(XRE_IsParentProcess(), "Only load scalars in the parent process"); - if (!XRE_IsParentProcess()) { - return NS_ERROR_FAILURE; - } - - typedef std::pair<nsCString, nsCOMPtr<nsIVariant>> PersistedScalarPair; - typedef nsTArray<PersistedScalarPair> PersistedScalarArray; - typedef nsTHashMap<ProcessIDHashKey, PersistedScalarArray> - PeristedScalarStorage; - - PeristedScalarStorage scalarsToUpdate; - - // Before updating the scalars, we need to get the data out of the JS - // wrappers. We can't hold the scalars mutex while handling JS stuff. - // Build a <scalar name, value> map. - JS::Rooted<JSObject*> scalarDataObj(aCx, &aData.toObject()); - JS::Rooted<JS::IdVector> processes(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, scalarDataObj, &processes)) { - // We can't even enumerate the processes in the loaded data, so - // there is nothing we could recover from the persistence file. Bail out. - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - // The following block of code attempts to extract as much data as possible - // from the serialized JSON, even in case of light data corruptions: if, for - // example, the data for a single process is corrupted or is in an unexpected - // form, we press on and attempt to load the data for the other processes. - JS::Rooted<JS::PropertyKey> process(aCx); - for (auto& processVal : processes) { - // This is required as JS API calls require an Handle<jsid> and not a - // plain jsid. - process = processVal; - // Get the process name. - nsAutoJSString processNameJS; - if (!processNameJS.init(aCx, process)) { - JS_ClearPendingException(aCx); - continue; - } - - // Make sure it's valid. Note that this is safe to call outside - // of a locked section. - NS_ConvertUTF16toUTF8 processName(processNameJS); - ProcessID processID = GetIDForProcessName(processName.get()); - if (processID == ProcessID::Count) { - NS_WARNING( - nsPrintfCString("Failed to get process ID for %s", processName.get()) - .get()); - continue; - } - - // And its probes. - JS::Rooted<JS::Value> processData(aCx); - if (!JS_GetPropertyById(aCx, scalarDataObj, process, &processData)) { - JS_ClearPendingException(aCx); - continue; - } - - if (!processData.isObject()) { - // |processData| should be an object containing scalars. If this is - // not the case, silently skip and try to load the data for the other - // processes. - continue; - } - - // Iterate through each scalar. - JS::Rooted<JSObject*> processDataObj(aCx, &processData.toObject()); - JS::Rooted<JS::IdVector> scalars(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, processDataObj, &scalars)) { - JS_ClearPendingException(aCx); - continue; - } - - JS::Rooted<JS::PropertyKey> scalar(aCx); - for (auto& scalarVal : scalars) { - scalar = scalarVal; - // Get the scalar name. - nsAutoJSString scalarName; - if (!scalarName.init(aCx, scalar)) { - JS_ClearPendingException(aCx); - continue; - } - - // Get the scalar value as a JS value. - JS::Rooted<JS::Value> scalarValue(aCx); - if (!JS_GetPropertyById(aCx, processDataObj, scalar, &scalarValue)) { - JS_ClearPendingException(aCx); - continue; - } - - if (scalarValue.isNullOrUndefined()) { - // We can't set scalars to null or undefined values, skip this - // and try to load other scalars. - continue; - } - - // Unpack the aVal to nsIVariant. - nsCOMPtr<nsIVariant> unpackedVal; - nsresult rv = nsContentUtils::XPConnect()->JSToVariant( - aCx, scalarValue, getter_AddRefs(unpackedVal)); - if (NS_FAILED(rv)) { - JS_ClearPendingException(aCx); - continue; - } - - // Add the scalar to the map. - PersistedScalarArray& processScalars = - scalarsToUpdate.LookupOrInsert(static_cast<uint32_t>(processID)); - processScalars.AppendElement(std::make_pair( - nsCString(NS_ConvertUTF16toUTF8(scalarName)), unpackedVal)); - } - } - - // Now that all the JS specific operations are finished, update the scalars. - { - StaticMutexAutoLock lock(gTelemetryScalarsMutex); - - for (const auto& entry : scalarsToUpdate) { - const PersistedScalarArray& processScalars = entry.GetData(); - for (PersistedScalarArray::size_type i = 0; i < processScalars.Length(); - i++) { - mozilla::Unused << internal_UpdateScalar( - lock, processScalars[i].first, ScalarActionType::eSet, - processScalars[i].second, ProcessID(entry.GetKey()), - true /* aForce */); - } - } - } - - return NS_OK; -} - -/** - * Load the persisted measurements from a Json object and injects them - * in the relevant process storage. - * - * @param {aData} The input Json object. - * @returns NS_OK if loading was performed, an error code explaining the - * failure reason otherwise. - */ -nsresult TelemetryScalar::DeserializePersistedKeyedScalars( - JSContext* aCx, JS::Handle<JS::Value> aData) { - MOZ_ASSERT(XRE_IsParentProcess(), "Only load scalars in the parent process"); - if (!XRE_IsParentProcess()) { - return NS_ERROR_FAILURE; - } - - typedef std::tuple<nsCString, nsString, nsCOMPtr<nsIVariant>> - PersistedKeyedScalarTuple; - typedef nsTArray<PersistedKeyedScalarTuple> PersistedKeyedScalarArray; - typedef nsTHashMap<ProcessIDHashKey, PersistedKeyedScalarArray> - PeristedKeyedScalarStorage; - - PeristedKeyedScalarStorage scalarsToUpdate; - - // Before updating the keyed scalars, we need to get the data out of the JS - // wrappers. We can't hold the scalars mutex while handling JS stuff. - // Build a <scalar name, value> map. - JS::Rooted<JSObject*> scalarDataObj(aCx, &aData.toObject()); - JS::Rooted<JS::IdVector> processes(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, scalarDataObj, &processes)) { - // We can't even enumerate the processes in the loaded data, so - // there is nothing we could recover from the persistence file. Bail out. - JS_ClearPendingException(aCx); - return NS_ERROR_FAILURE; - } - - // The following block of code attempts to extract as much data as possible - // from the serialized JSON, even in case of light data corruptions: if, for - // example, the data for a single process is corrupted or is in an unexpected - // form, we press on and attempt to load the data for the other processes. - JS::Rooted<JS::PropertyKey> process(aCx); - for (auto& processVal : processes) { - process = processVal; - // Get the process name. - nsAutoJSString processNameJS; - if (!processNameJS.init(aCx, process)) { - JS_ClearPendingException(aCx); - continue; - } - - // Make sure it's valid. Note that this is safe to call outside - // of a locked section. - NS_ConvertUTF16toUTF8 processName(processNameJS); - ProcessID processID = GetIDForProcessName(processName.get()); - if (processID == ProcessID::Count) { - NS_WARNING( - nsPrintfCString("Failed to get process ID for %s", processName.get()) - .get()); - continue; - } - - // And its probes. - JS::Rooted<JS::Value> processData(aCx); - if (!JS_GetPropertyById(aCx, scalarDataObj, process, &processData)) { - JS_ClearPendingException(aCx); - continue; - } - - if (!processData.isObject()) { - // |processData| should be an object containing scalars. If this is - // not the case, silently skip and try to load the data for the other - // processes. - continue; - } - - // Iterate through each keyed scalar. - JS::Rooted<JSObject*> processDataObj(aCx, &processData.toObject()); - JS::Rooted<JS::IdVector> keyedScalars(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, processDataObj, &keyedScalars)) { - JS_ClearPendingException(aCx); - continue; - } - - JS::Rooted<JS::PropertyKey> keyedScalar(aCx); - for (auto& keyedScalarVal : keyedScalars) { - keyedScalar = keyedScalarVal; - // Get the scalar name. - nsAutoJSString scalarName; - if (!scalarName.init(aCx, keyedScalar)) { - JS_ClearPendingException(aCx); - continue; - } - - // Get the data for this keyed scalar. - JS::Rooted<JS::Value> keyedScalarData(aCx); - if (!JS_GetPropertyById(aCx, processDataObj, keyedScalar, - &keyedScalarData)) { - JS_ClearPendingException(aCx); - continue; - } - - if (!keyedScalarData.isObject()) { - // Keyed scalar data need to be an object. If that's not the case, skip - // it and try to load the rest of the data. - continue; - } - - // Get the keys in the keyed scalar. - JS::Rooted<JSObject*> keyedScalarDataObj(aCx, - &keyedScalarData.toObject()); - JS::Rooted<JS::IdVector> keys(aCx, JS::IdVector(aCx)); - if (!JS_Enumerate(aCx, keyedScalarDataObj, &keys)) { - JS_ClearPendingException(aCx); - continue; - } - - JS::Rooted<JS::PropertyKey> key(aCx); - for (auto keyVal : keys) { - key = keyVal; - // Get the process name. - nsAutoJSString keyName; - if (!keyName.init(aCx, key)) { - JS_ClearPendingException(aCx); - continue; - } - - // Get the scalar value as a JS value. - JS::Rooted<JS::Value> scalarValue(aCx); - if (!JS_GetPropertyById(aCx, keyedScalarDataObj, key, &scalarValue)) { - JS_ClearPendingException(aCx); - continue; - } - - if (scalarValue.isNullOrUndefined()) { - // We can't set scalars to null or undefined values, skip this - // and try to load other scalars. - continue; - } - - // Unpack the aVal to nsIVariant. - nsCOMPtr<nsIVariant> unpackedVal; - nsresult rv = nsContentUtils::XPConnect()->JSToVariant( - aCx, scalarValue, getter_AddRefs(unpackedVal)); - if (NS_FAILED(rv)) { - JS_ClearPendingException(aCx); - continue; - } - - // Add the scalar to the map. - PersistedKeyedScalarArray& processScalars = - scalarsToUpdate.LookupOrInsert(static_cast<uint32_t>(processID)); - processScalars.AppendElement( - std::make_tuple(nsCString(NS_ConvertUTF16toUTF8(scalarName)), - nsString(keyName), unpackedVal)); - } - } - } - - // Now that all the JS specific operations are finished, update the scalars. - { - StaticMutexAutoLock lock(gTelemetryScalarsMutex); - - for (const auto& entry : scalarsToUpdate) { - const PersistedKeyedScalarArray& processScalars = entry.GetData(); - for (PersistedKeyedScalarArray::size_type i = 0; - i < processScalars.Length(); i++) { - mozilla::Unused << internal_UpdateKeyedScalar( - lock, std::get<0>(processScalars[i]), - std::get<1>(processScalars[i]), ScalarActionType::eSet, - std::get<2>(processScalars[i]), ProcessID(entry.GetKey()), - true /* aForce */); - } - } - } - - return NS_OK; -} diff --git a/toolkit/components/telemetry/core/TelemetryScalar.h b/toolkit/components/telemetry/core/TelemetryScalar.h index c7e5352860..d45d12cca5 100644 --- a/toolkit/components/telemetry/core/TelemetryScalar.h +++ b/toolkit/components/telemetry/core/TelemetryScalar.h @@ -18,8 +18,6 @@ // For the public interface to Telemetry functionality, see Telemetry.h. namespace mozilla { -// This is only used for the GeckoView persistence. -class JSONWriter; namespace Telemetry { struct ScalarAction; struct KeyedScalarAction; @@ -112,22 +110,6 @@ void AddDynamicScalarDefinitions( * This includes dynamic stores. */ nsresult GetAllStores(mozilla::Telemetry::Common::StringHashSet& set); - -// They are responsible for updating in-memory probes with the data persisted -// on the disk and vice-versa. -nsresult SerializeScalars(mozilla::JSONWriter& aWriter); -nsresult SerializeKeyedScalars(mozilla::JSONWriter& aWriter); -nsresult DeserializePersistedScalars(JSContext* aCx, - JS::Handle<JS::Value> aData); -nsresult DeserializePersistedKeyedScalars(JSContext* aCx, - JS::Handle<JS::Value> aData); -// Mark deserialization as in progress. -// After this, all scalar operations are recorded into the pending operations -// list. -void DeserializationStarted(); -// Apply all operations from the pending operations list and mark -// deserialization finished afterwards. -void ApplyPendingOperations(); } // namespace TelemetryScalar #endif // TelemetryScalar_h__ |