diff options
Diffstat (limited to 'toolkit/components/telemetry/core/TelemetryScalar.cpp')
-rw-r--r-- | toolkit/components/telemetry/core/TelemetryScalar.cpp | 794 |
1 files changed, 14 insertions, 780 deletions
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; -} |