diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:29 +0000 |
commit | 59203c63bb777a3bacec32fb8830fba33540e809 (patch) | |
tree | 58298e711c0ff0575818c30485b44a2f21bf28a0 /toolkit/components/telemetry | |
parent | Adding upstream version 126.0.1. (diff) | |
download | firefox-59203c63bb777a3bacec32fb8830fba33540e809.tar.xz firefox-59203c63bb777a3bacec32fb8830fba33540e809.zip |
Adding upstream version 127.0.upstream/127.0
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/telemetry')
39 files changed, 263 insertions, 2578 deletions
diff --git a/toolkit/components/telemetry/Events.yaml b/toolkit/components/telemetry/Events.yaml index 5d3d433a05..339fd1feb8 100644 --- a/toolkit/components/telemetry/Events.yaml +++ b/toolkit/components/telemetry/Events.yaml @@ -294,33 +294,6 @@ addonsManager: record_in_processes: ["main"] bug_numbers: [1433335, 1515697, 1523641, 1549770, 1590736, 1630596, 1672570, 1714251, 1749878, 1781974, 1817100, 1861295] release_channel_collection: opt-out - report: - description: > - An abuse report submitted by a user for a given extension. The object of the event - represent the report entry point, the value is the id of the addon being reported. - objects: - - amo - - menu - - toolbar_context_menu - - unified_context_menu - - uninstall - extra_keys: - addon_type: > - The type of the add-on being reported (missing on ERROR_ADDON_NOT_FOUND, ERROR_AMODETAILS_NOTFOUND - and ERROR_AMODETAILS_FAILURE). - error_type: > - AbuseReport Error Type (included in case of submission failures). The error types include - ERROR_ABORTED_SUBMIT, ERROR_ADDON_NOT_FOUND, ERROR_CLIENT, ERROR_NETWORK, ERROR_UNKNOWN, - ERROR_RECENT_SUBMIT, ERROR_SERVER, ERROR_AMODETAILS_NOTFOUND, ERROR_AMODETAILS_FAILURE. - notification_emails: ["addons-dev-internal@mozilla.com"] - expiry_version: "132" - products: - - "firefox" - - "fennec" - - "thunderbird" - record_in_processes: ["main"] - bug_numbers: [1544927, 1580561, 1590736, 1630596, 1672570, 1714251, 1749878, 1780746, 1781974, 1817100, 1861295] - release_channel_collection: opt-out reportSuspiciousSite: description: > Sent when a user clicks "Report Suspicious Site" on the dropdown menu of the third- diff --git a/toolkit/components/telemetry/Histograms.json b/toolkit/components/telemetry/Histograms.json index 4f0f0e2329..db58482aad 100644 --- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -8944,7 +8944,7 @@ "record_in_processes": ["main"], "products": ["firefox"], "alert_emails": ["fx-search-telemetry@mozilla.com"], - "expires_in_version": "128", + "expires_in_version": "131", "kind": "exponential", "low": 16, "high": 5000, @@ -9738,7 +9738,7 @@ }, "TELEMETRY_TEST_CATEGORICAL_OPTOUT": { "record_in_processes": ["main", "content"], - "products": ["firefox", "fennec", "geckoview_streaming", "thunderbird"], + "products": ["firefox", "fennec", "thunderbird"], "alert_emails": ["telemetry-client-dev@mozilla.com"], "bug_numbers": [1188888], "expires_in_version": "never", @@ -9956,7 +9956,7 @@ }, "TELEMETRY_TEST_EXPIRED": { "record_in_processes": ["main", "content"], - "products": ["firefox", "fennec", "thunderbird", "geckoview_streaming"], + "products": ["firefox", "fennec", "thunderbird"], "alert_emails": ["telemetry-client-dev@mozilla.com"], "expires_in_version": "4", "kind": "linear", @@ -10211,32 +10211,6 @@ "description": "a testing histogram; not meant to be touched", "record_into_store": ["main", "sync"] }, - "TELEMETRY_TEST_STREAMING": { - "record_in_processes": ["main"], - "products": ["geckoview_streaming"], - "alert_emails": ["telemetry-client-dev@mozilla.com"], - "expires_in_version": "never", - "kind": "linear", - "low": 1, - "high": 2147483646, - "n_buckets": 10, - "releaseChannelCollection": "opt-out", - "bug_numbers": [1566366], - "description": "a testing histogram; not meant to be touched" - }, - "TELEMETRY_TEST_STREAMING_2": { - "record_in_processes": ["main"], - "products": ["geckoview_streaming"], - "alert_emails": ["telemetry-client-dev@mozilla.com"], - "expires_in_version": "never", - "kind": "linear", - "low": 1, - "high": 2147483646, - "n_buckets": 10, - "releaseChannelCollection": "opt-out", - "bug_numbers": [1566366], - "description": "a testing histogram; not meant to be touched" - }, "STARTUP_CRASH_DETECTED": { "record_in_processes": ["main", "content"], "products": ["firefox", "fennec", "thunderbird"], @@ -16096,68 +16070,6 @@ "description": "The amount of time it takes for a PageAction popup to open, keyed by addon id.", "keyed": true }, - "WEBEXT_STORAGE_LOCAL_GET_MS": { - "record_in_processes": ["main", "content"], - "products": ["firefox", "fennec"], - "alert_emails": ["addons-dev-internal@mozilla.com", "lgreco@mozilla.com"], - "bug_numbers": [ - 1371398, 1513556, 1578225, 1623315, 1666980, 1706839, 1745271, 1777402, - 1811155, 1861303 - ], - "expires_in_version": "132", - "kind": "exponential", - "releaseChannelCollection": "opt-out", - "high": 50000, - "n_buckets": 100, - "description": "The amount of time it takes to perform a get via storage.local using the JSONFile backend." - }, - "WEBEXT_STORAGE_LOCAL_GET_MS_BY_ADDONID": { - "record_in_processes": ["main", "content"], - "products": ["firefox", "fennec"], - "alert_emails": ["addons-dev-internal@mozilla.com", "lgreco@mozilla.com"], - "bug_numbers": [ - 1483002, 1513556, 1578225, 1623315, 1666980, 1706839, 1745271, 1777402, - 1811155, 1861303 - ], - "expires_in_version": "132", - "kind": "exponential", - "releaseChannelCollection": "opt-out", - "high": 50000, - "n_buckets": 100, - "description": "The amount of time it takes to perform a get via storage.local using the JSONFile backend, keyed by addon id.", - "keyed": true - }, - "WEBEXT_STORAGE_LOCAL_SET_MS": { - "record_in_processes": ["main", "content"], - "products": ["firefox", "fennec"], - "alert_emails": ["addons-dev-internal@mozilla.com", "lgreco@mozilla.com"], - "bug_numbers": [ - 1371398, 1513556, 1578225, 1623315, 1666980, 1706839, 1745271, 1777402, - 1811155, 1861303 - ], - "expires_in_version": "132", - "kind": "exponential", - "releaseChannelCollection": "opt-out", - "high": 50000, - "n_buckets": 100, - "description": "The amount of time it takes to perform a set via storage.local using the JSONFile backend." - }, - "WEBEXT_STORAGE_LOCAL_SET_MS_BY_ADDONID": { - "record_in_processes": ["main", "content"], - "products": ["firefox", "fennec"], - "alert_emails": ["addons-dev-internal@mozilla.com", "lgreco@mozilla.com"], - "bug_numbers": [ - 1483002, 1513556, 1578225, 1623315, 1666980, 1706839, 1745271, 1777402, - 1811155, 1861303 - ], - "expires_in_version": "132", - "kind": "exponential", - "releaseChannelCollection": "opt-out", - "high": 50000, - "n_buckets": 100, - "description": "The amount of time it takes to perform a set via storage.local using the JSONFile backend, keyed by addon id.", - "keyed": true - }, "WEBEXT_STORAGE_LOCAL_IDB_GET_MS": { "record_in_processes": ["main", "content"], "products": ["firefox", "fennec"], @@ -16863,26 +16775,6 @@ "n_buckets": 32, "description": "The number of style and layout flushes that occur per Refresh Driver tick." }, - "IDB_CUSTOM_OPEN_WITH_OPTIONS_COUNT": { - "record_in_processes": ["main", "content"], - "products": ["firefox", "fennec"], - "bug_numbers": [1566758], - "alert_emails": ["sgiesecke@mozilla.com"], - "expires_in_version": "74", - "kind": "categorical", - "labels": [ - "system", - "content_file", - "content_http_https", - "content_moz_ext", - "content_about", - "content_other", - "expanded", - "other" - ], - "releaseChannelCollection": "opt-out", - "description": "Tracking the use of the custom IDBFactory.open overload accepting an options dictionary in one of the categories." - }, "PRESSHELL_LAYOUT_TOTAL_MS_PER_TICK": { "record_in_processes": ["main", "content"], "products": ["firefox", "fennec"], diff --git a/toolkit/components/telemetry/Scalars.yaml b/toolkit/components/telemetry/Scalars.yaml index f63b8b5fdb..0822865b4c 100644 --- a/toolkit/components/telemetry/Scalars.yaml +++ b/toolkit/components/telemetry/Scalars.yaml @@ -1786,9 +1786,10 @@ extensions.startupCache: write_byteLength: bug_numbers: - 1767336 + - 1892151 description: > The amount of bytes writted into the Extensions StartupCache file. - expires: "128" + expires: "135" kind: uint notification_emails: - addons-dev-internal@mozilla.com @@ -1800,10 +1801,11 @@ extensions.startupCache: read_errors: bug_numbers: - 1767336 + - 1892151 description: > The amount of times an unexpected error has been raised while reading the Extensions StartupCache file - expires: "128" + expires: "135" kind: uint keyed: true notification_emails: @@ -2947,6 +2949,7 @@ media: device_hardware_decoding_support: bug_numbers: - 1850594 + - 1892516 description: > Record the hardware decoding availability on devices, the key is the name of video codec, eg. h264, av1. expires: never @@ -2958,7 +2961,7 @@ media: products: - 'firefox' record_in_processes: - - 'gpu' + - 'main' # The following section contains content process base counters. dom.contentprocess: @@ -8789,24 +8792,6 @@ contextual.services.quicksuggest: - 'firefox' record_in_processes: - main - help_dynamic_wikipedia: - bug_numbers: - - 1800993 - description: > - A keyed uint recording how many times the user has clicked on the help - button in sponsored Firefox Suggest dynamic wikipedia results - in the urlbar (not including the help button). The key is the 1-based - index of each result. - expires: never - kind: uint - keyed: true - notification_emails: - - fx-search-telemetry@mozilla.com - release_channel_collection: opt-out - products: - - 'firefox' - record_in_processes: - - main help_nonsponsored: bug_numbers: - 1800993 @@ -8841,23 +8826,6 @@ contextual.services.quicksuggest: - 'firefox' record_in_processes: - main - help_weather: - bug_numbers: - - 1804536 - description: > - A keyed uint recording how many times the user has clicked on the help - button in Firefox Suggests (a.k.a. Quick Suggest) weather results in the - urlbar. The key is the 1-based index of each result. - expires: never - kind: uint - keyed: true - notification_emails: - - fx-search-telemetry@mozilla.com - release_channel_collection: opt-out - products: - - 'firefox' - record_in_processes: - - main block_dynamic_wikipedia: bug_numbers: - 1800993 @@ -9198,7 +9166,6 @@ telemetry.test: products: - 'firefox' - 'fennec' - - 'geckoview_streaming' - 'thunderbird' record_in_processes: - 'main' # test_ChildScalars.js depends on this being main-only. @@ -9216,7 +9183,6 @@ telemetry.test: products: - 'firefox' - 'fennec' - - 'geckoview_streaming' - 'thunderbird' record_in_processes: - 'main' @@ -9234,7 +9200,6 @@ telemetry.test: products: - 'firefox' - 'fennec' - - 'geckoview_streaming' - 'thunderbird' record_in_processes: - 'main' diff --git a/toolkit/components/telemetry/app/TelemetryControllerParent.sys.mjs b/toolkit/components/telemetry/app/TelemetryControllerParent.sys.mjs index b9f8d5f85a..9a15142349 100644 --- a/toolkit/components/telemetry/app/TelemetryControllerParent.sys.mjs +++ b/toolkit/components/telemetry/app/TelemetryControllerParent.sys.mjs @@ -256,6 +256,15 @@ export var TelemetryController = Object.freeze({ promiseInitialized() { return Impl.promiseInitialized(); }, + + /** + * Allows to trigger TelemetryControllers delayed initialization now and waiting for its completion. + * The returned promise is guaranteed to resolve before TelemetryController is shutting down. + * @return {Promise} Resolved when delayed TelemetryController initialization completed. + */ + ensureInitialized() { + return Impl.ensureInitialized(); + }, }); var Impl = { @@ -1158,6 +1167,18 @@ var Impl = { return this._delayedInitTaskDeferred.promise; }, + /** + * Allows to trigger TelemetryControllers delayed initialization now and waiting for its completion. + * This will complete before TelemetryController is shutting down. + * @return {Promise} Resolved when delayed TelemetryController initialization completed. + */ + ensureInitialized() { + if (this._delayedInitTask) { + return this._delayedInitTask.finalize(); + } + return Promise.resolve(); + }, + getCurrentPingData(aSubsession) { this._log.trace("getCurrentPingData - subsession: " + aSubsession); diff --git a/toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs b/toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs index 18d46a3565..5b917293c1 100644 --- a/toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs +++ b/toolkit/components/telemetry/app/TelemetryEnvironment.sys.mjs @@ -895,6 +895,8 @@ EnvironmentAddonBuilder.prototype = { hasBinaryComponents: false, installDay: Utils.millisecondsToDays(installDate.getTime()), updateDay: Utils.millisecondsToDays(updateDate.getTime()), + signedState: theme.signedState, + signedTypes: JSON.stringify(theme.signedTypes), }; } @@ -1672,6 +1674,7 @@ EnvironmentCache.prototype = { let creationDate = await profileAccessor.created; let resetDate = await profileAccessor.reset; let firstUseDate = await profileAccessor.firstUse; + let recoveredFromBackup = await profileAccessor.recoveredFromBackup; this._currentEnvironment.profile.creationDate = Utils.millisecondsToDays(creationDate); @@ -1683,6 +1686,10 @@ EnvironmentCache.prototype = { this._currentEnvironment.profile.firstUseDate = Utils.millisecondsToDays(firstUseDate); } + if (recoveredFromBackup) { + this._currentEnvironment.profile.recoveredFromBackup = + Utils.millisecondsToDays(recoveredFromBackup); + } }, /** diff --git a/toolkit/components/telemetry/build_scripts/mozparsers/parse_events.py b/toolkit/components/telemetry/build_scripts/mozparsers/parse_events.py index 09ed651917..1275e03a5e 100644 --- a/toolkit/components/telemetry/build_scripts/mozparsers/parse_events.py +++ b/toolkit/components/telemetry/build_scripts/mozparsers/parse_events.py @@ -265,12 +265,6 @@ class EventData: ParserError( self.identifier + ": Unknown value in products: " + product ).handle_later() - if utils.is_geckoview_streaming_product(product): - ParserError( - "{}: Product `{}` unsupported for Event Telemetry".format( - self.identifier, product - ) - ).handle_later() # Check operating_systems. operating_systems = definition.get("operating_systems", []) diff --git a/toolkit/components/telemetry/build_scripts/mozparsers/parse_histograms.py b/toolkit/components/telemetry/build_scripts/mozparsers/parse_histograms.py index 626188bf06..92fff31a48 100644 --- a/toolkit/components/telemetry/build_scripts/mozparsers/parse_histograms.py +++ b/toolkit/components/telemetry/build_scripts/mozparsers/parse_histograms.py @@ -46,12 +46,6 @@ BASE_DOC_URL = ( HISTOGRAMS_DOC_URL = BASE_DOC_URL + "collection/histograms.html" SCALARS_DOC_URL = BASE_DOC_URL + "collection/scalars.html" -GECKOVIEW_STREAMING_SUPPORTED_KINDS = [ - "linear", - "exponential", - "categorical", -] - def linear_buckets(dmin, dmax, n_buckets): ret_array = [0] * n_buckets @@ -435,22 +429,6 @@ class Histogram: 'Histogram "%s" has unknown product "%s" in %s.\n%s' % (name, product, field, DOC_URL) ).handle_later() - if utils.is_geckoview_streaming_product(product): - kind = definition.get("kind") - if kind not in GECKOVIEW_STREAMING_SUPPORTED_KINDS: - ParserError( - ( - 'Histogram "%s" is of kind "%s" which is unsupported for ' - 'product "%s".' - ) - % (name, kind, product) - ).handle_later() - keyed = definition.get("keyed") - if keyed: - ParserError( - 'Keyed histograms like "%s" are unsupported for product "%s"' - % (name, product) - ).handle_later() def check_operating_systems(self, name, definition): if not self._strict_type_checks: diff --git a/toolkit/components/telemetry/build_scripts/mozparsers/parse_scalars.py b/toolkit/components/telemetry/build_scripts/mozparsers/parse_scalars.py index 5ec591b393..b46cc7c3db 100644 --- a/toolkit/components/telemetry/build_scripts/mozparsers/parse_scalars.py +++ b/toolkit/components/telemetry/build_scripts/mozparsers/parse_scalars.py @@ -308,13 +308,6 @@ class ScalarType: + product + ".\nSee: {}".format(BASE_DOC_URL) ).handle_later() - if utils.is_geckoview_streaming_product(product): - keyed = definition.get("keyed") - if keyed: - ParserError( - "%s - keyed Scalars not supported for product %s" - % (self._name, product) - ).handle_later() # Validate the expiration version. # Historical versions of Scalars.json may contain expiration versions diff --git a/toolkit/components/telemetry/build_scripts/mozparsers/shared_telemetry_utils.py b/toolkit/components/telemetry/build_scripts/mozparsers/shared_telemetry_utils.py index 4b4cc9f685..80ae685c45 100644 --- a/toolkit/components/telemetry/build_scripts/mozparsers/shared_telemetry_utils.py +++ b/toolkit/components/telemetry/build_scripts/mozparsers/shared_telemetry_utils.py @@ -26,15 +26,13 @@ KNOWN_PROCESS_FLAGS = { "all_childs": "AllChildren", # Supporting files from before bug 1363725 } -GECKOVIEW_STREAMING_PRODUCT = "geckoview_streaming" - SUPPORTED_PRODUCTS = { "firefox": "Firefox", "fennec": "Fennec", - GECKOVIEW_STREAMING_PRODUCT: "GeckoviewStreaming", "thunderbird": "Thunderbird", # Historical, deprecated values: # 'geckoview': 'Geckoview', + # "geckoview_streaming": "GeckoviewStreaming", } SUPPORTED_OPERATING_SYSTEMS = [ @@ -114,10 +112,6 @@ def is_valid_product(name): return name in SUPPORTED_PRODUCTS -def is_geckoview_streaming_product(name): - return name == GECKOVIEW_STREAMING_PRODUCT - - def is_valid_os(name): return name in SUPPORTED_OPERATING_SYSTEMS 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__ diff --git a/toolkit/components/telemetry/dap/DAPTelemetrySender.sys.mjs b/toolkit/components/telemetry/dap/DAPTelemetrySender.sys.mjs index 9dcc949788..b948e80681 100644 --- a/toolkit/components/telemetry/dap/DAPTelemetrySender.sys.mjs +++ b/toolkit/components/telemetry/dap/DAPTelemetrySender.sys.mjs @@ -35,7 +35,12 @@ XPCOMUtils.defineLazyPreferenceGetter(lazy, "HELPER", PREF_HELPER, undefined); */ export const DAPTelemetrySender = new (class { - startup() { + async startup() { + await lazy.NimbusFeatures.dapTelemetry.ready(); + if (!lazy.NimbusFeatures.dapTelemetry.getVariable("enabled")) { + return; + } + lazy.logConsole.debug("Performing DAP startup"); if (lazy.NimbusFeatures.dapTelemetry.getVariable("visitCountingEnabled")) { diff --git a/toolkit/components/telemetry/dap/ffi-gtest/Cargo.toml b/toolkit/components/telemetry/dap/ffi-gtest/Cargo.toml index abad140b7d..3abbb9d60c 100644 --- a/toolkit/components/telemetry/dap/ffi-gtest/Cargo.toml +++ b/toolkit/components/telemetry/dap/ffi-gtest/Cargo.toml @@ -11,7 +11,7 @@ edition = "2021" [dependencies] dap_ffi = { path = "../ffi" } hex = { version = "0.4.3", features = ["serde"] } -prio = {version = "0.15.3", default-features = false } +prio = {version = "0.16.2", default-features = false } serde = { version = "1.0", features = ["derive"] } serde_json = { version = "1.0" } thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } diff --git a/toolkit/components/telemetry/dap/ffi-gtest/test.rs b/toolkit/components/telemetry/dap/ffi-gtest/test.rs index 3871965107..874e0e99a5 100644 --- a/toolkit/components/telemetry/dap/ffi-gtest/test.rs +++ b/toolkit/components/telemetry/dap/ffi-gtest/test.rs @@ -17,7 +17,7 @@ use prio::codec::{Decode, Encode}; pub extern "C" fn dap_test_encoding() { let r = Report::new_dummy(); let mut encoded = Vec::<u8>::new(); - Report::encode(&r, &mut encoded); + Report::encode(&r, &mut encoded).expect("Report encoding failed!"); let decoded = Report::decode(&mut Cursor::new(&encoded)).expect("Report decoding failed!"); if r != decoded { println!("Report:"); diff --git a/toolkit/components/telemetry/dap/ffi/Cargo.toml b/toolkit/components/telemetry/dap/ffi/Cargo.toml index dfb69e4821..5e94bf30a7 100644 --- a/toolkit/components/telemetry/dap/ffi/Cargo.toml +++ b/toolkit/components/telemetry/dap/ffi/Cargo.toml @@ -8,6 +8,7 @@ authors = [ license = "MPL-2.0" [dependencies] -prio = {version = "0.15.3", default-features = false } +prio = {version = "0.16.2", default-features = false } thin-vec = { version = "0.2.1", features = ["gecko-ffi"] } rand = "0.8" +log = "0.4" diff --git a/toolkit/components/telemetry/dap/ffi/src/lib.rs b/toolkit/components/telemetry/dap/ffi/src/lib.rs index 998c8af204..2d595c1a8f 100644 --- a/toolkit/components/telemetry/dap/ffi/src/lib.rs +++ b/toolkit/components/telemetry/dap/ffi/src/lib.rs @@ -5,6 +5,8 @@ use std::error::Error; use std::io::Cursor; +use log::{debug, warn}; + use prio::vdaf::prio3::Prio3Sum; use prio::vdaf::prio3::Prio3SumVec; use thin_vec::ThinVec; @@ -19,8 +21,6 @@ use types::Time; use prio::codec::Encode; use prio::codec::{decode_u16_items, encode_u32_items}; -use prio::flp::types::{Sum, SumVec}; -use prio::vdaf::prio3::Prio3; use prio::vdaf::Client; use prio::vdaf::VdafError; @@ -41,7 +41,7 @@ extern "C" { ) -> bool; } -pub fn new_prio_u8(num_aggregators: u8, bits: u32) -> Result<Prio3Sum, VdafError> { +pub fn new_prio_sum(num_aggregators: u8, bits: usize) -> Result<Prio3Sum, VdafError> { if bits > 64 { return Err(VdafError::Uncategorized(format!( "bit length ({}) exceeds limit for aggregate type (64)", @@ -49,17 +49,16 @@ pub fn new_prio_u8(num_aggregators: u8, bits: u32) -> Result<Prio3Sum, VdafError ))); } - Prio3::new(num_aggregators, Sum::new(bits as usize)?) + Prio3Sum::new_sum(num_aggregators, bits) } -pub fn new_prio_vecu8(num_aggregators: u8, len: usize) -> Result<Prio3SumVec, VdafError> { +pub fn new_prio_sumvec( + num_aggregators: u8, + len: usize, + bits: usize, +) -> Result<Prio3SumVec, VdafError> { let chunk_length = prio::vdaf::prio3::optimal_chunk_length(8 * len); - Prio3::new(num_aggregators, SumVec::new(8, len, chunk_length)?) -} - -pub fn new_prio_vecu16(num_aggregators: u8, len: usize) -> Result<Prio3SumVec, VdafError> { - let chunk_length = prio::vdaf::prio3::optimal_chunk_length(16 * len); - Prio3::new(num_aggregators, SumVec::new(16, len, chunk_length)?) + Prio3SumVec::new_sum_vec(num_aggregators, bits, len, chunk_length) } enum Role { @@ -112,14 +111,17 @@ impl Shardable for u8 { &self, nonce: &[u8; 16], ) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn std::error::Error>> { - let prio = new_prio_u8(2, 2)?; + let prio = new_prio_sum(2, 8)?; let (public_share, input_shares) = prio.shard(&(*self as u128), nonce)?; debug_assert_eq!(input_shares.len(), 2); - let encoded_input_shares = input_shares.iter().map(|s| s.get_encoded()).collect(); - let encoded_public_share = public_share.get_encoded(); + let encoded_input_shares = input_shares + .iter() + .map(|s| s.get_encoded()) + .collect::<Result<Vec<_>, _>>()?; + let encoded_public_share = public_share.get_encoded()?; Ok((encoded_public_share, encoded_input_shares)) } } @@ -129,15 +131,18 @@ impl Shardable for ThinVec<u8> { &self, nonce: &[u8; 16], ) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn std::error::Error>> { - let prio = new_prio_vecu8(2, self.len())?; + let prio = new_prio_sumvec(2, self.len(), 8)?; let measurement: Vec<u128> = self.iter().map(|e| (*e as u128)).collect(); let (public_share, input_shares) = prio.shard(&measurement, nonce)?; debug_assert_eq!(input_shares.len(), 2); - let encoded_input_shares = input_shares.iter().map(|s| s.get_encoded()).collect(); - let encoded_public_share = public_share.get_encoded(); + let encoded_input_shares = input_shares + .iter() + .map(|s| s.get_encoded()) + .collect::<Result<Vec<_>, _>>()?; + let encoded_public_share = public_share.get_encoded()?; Ok((encoded_public_share, encoded_input_shares)) } } @@ -147,15 +152,18 @@ impl Shardable for ThinVec<u16> { &self, nonce: &[u8; 16], ) -> Result<(Vec<u8>, Vec<Vec<u8>>), Box<dyn std::error::Error>> { - let prio = new_prio_vecu16(2, self.len())?; + let prio = new_prio_sumvec(2, self.len(), 16)?; let measurement: Vec<u128> = self.iter().map(|e| (*e as u128)).collect(); let (public_share, input_shares) = prio.shard(&measurement, nonce)?; debug_assert_eq!(input_shares.len(), 2); - let encoded_input_shares = input_shares.iter().map(|s| s.get_encoded()).collect(); - let encoded_public_share = public_share.get_encoded(); + let encoded_input_shares = input_shares + .iter() + .map(|s| s.get_encoded()) + .collect::<Result<Vec<_>, _>>()?; + let encoded_public_share = public_share.get_encoded()?; Ok((encoded_public_share, encoded_input_shares)) } } @@ -163,7 +171,7 @@ impl Shardable for ThinVec<u16> { /// Pre-fill the info part of the HPKE sealing with the constants from the standard. fn make_base_info() -> Vec<u8> { let mut info = Vec::<u8>::new(); - const START: &[u8] = "dap-07 input share".as_bytes(); + const START: &[u8] = "dap-09 input share".as_bytes(); info.extend(START); const FIXED: u8 = 1; info.push(FIXED); @@ -215,7 +223,8 @@ fn get_dap_report_internal<T: Shardable>( } .get_encoded() }) - .collect(); + .collect::<Result<Vec<_>, _>>()?; + debug!("Plaintext input shares computed."); let metadata = ReportMetadata { report_id, @@ -230,18 +239,20 @@ fn get_dap_report_internal<T: Shardable>( let mut info = make_base_info(); let mut aad = Vec::from(*task_id); - metadata.encode(&mut aad); - encode_u32_items(&mut aad, &(), &encoded_public_share); + metadata.encode(&mut aad)?; + encode_u32_items(&mut aad, &(), &encoded_public_share)?; info.push(Role::Leader as u8); let leader_payload = hpke_encrypt_wrapper(&plaintext_input_shares[0], &aad, &info, &leader_hpke_config)?; + debug!("Leader payload encrypted."); *info.last_mut().unwrap() = Role::Helper as u8; let helper_payload = hpke_encrypt_wrapper(&plaintext_input_shares[1], &aad, &info, &helper_hpke_config)?; + debug!("Helper payload encrypted."); Ok(Report { metadata, @@ -264,20 +275,22 @@ pub extern "C" fn dapGetReportU8( ) -> bool { assert_eq!(task_id.len(), 32); - if let Ok(report) = get_dap_report_internal::<u8>( + let Ok(report) = get_dap_report_internal::<u8>( leader_hpke_config_encoded, helper_hpke_config_encoded, &measurement, &task_id.as_slice().try_into().unwrap(), time_precision, - ) { - let encoded_report = report.get_encoded(); - out_report.extend(encoded_report); - - true - } else { - false - } + ) else { + warn!("Creating report failed!"); + return false; + }; + let Ok(encoded_report) = report.get_encoded() else { + warn!("Encoding report failed!"); + return false; + }; + out_report.extend(encoded_report); + true } #[no_mangle] @@ -291,20 +304,22 @@ pub extern "C" fn dapGetReportVecU8( ) -> bool { assert_eq!(task_id.len(), 32); - if let Ok(report) = get_dap_report_internal::<ThinVec<u8>>( + let Ok(report) = get_dap_report_internal::<ThinVec<u8>>( leader_hpke_config_encoded, helper_hpke_config_encoded, measurement, &task_id.as_slice().try_into().unwrap(), time_precision, - ) { - let encoded_report = report.get_encoded(); - out_report.extend(encoded_report); - - true - } else { - false - } + ) else { + warn!("Creating report failed!"); + return false; + }; + let Ok(encoded_report) = report.get_encoded() else { + warn!("Encoding report failed!"); + return false; + }; + out_report.extend(encoded_report); + true } #[no_mangle] @@ -318,18 +333,20 @@ pub extern "C" fn dapGetReportVecU16( ) -> bool { assert_eq!(task_id.len(), 32); - if let Ok(report) = get_dap_report_internal::<ThinVec<u16>>( + let Ok(report) = get_dap_report_internal::<ThinVec<u16>>( leader_hpke_config_encoded, helper_hpke_config_encoded, measurement, &task_id.as_slice().try_into().unwrap(), time_precision, - ) { - let encoded_report = report.get_encoded(); - out_report.extend(encoded_report); - - true - } else { - false - } + ) else { + warn!("Creating report failed!"); + return false; + }; + let Ok(encoded_report) = report.get_encoded() else { + warn!("Encoding report failed!"); + return false; + }; + out_report.extend(encoded_report); + true } diff --git a/toolkit/components/telemetry/dap/ffi/src/types.rs b/toolkit/components/telemetry/dap/ffi/src/types.rs index e8f6385dcd..c84cbf16bc 100644 --- a/toolkit/components/telemetry/dap/ffi/src/types.rs +++ b/toolkit/components/telemetry/dap/ffi/src/types.rs @@ -34,8 +34,9 @@ impl Decode for TaskID { } impl Encode for TaskID { - fn encode(&self, bytes: &mut Vec<u8>) { + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { bytes.extend_from_slice(&self.0); + Ok(()) } } @@ -52,8 +53,9 @@ impl Decode for Time { } impl Encode for Time { - fn encode(&self, bytes: &mut Vec<u8>) { - u64::encode(&self.0, bytes); + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { + u64::encode(&self.0, bytes)?; + Ok(()) } } @@ -93,9 +95,10 @@ impl Decode for Extension { } impl Encode for Extension { - fn encode(&self, bytes: &mut Vec<u8>) { - (self.extension_type as u16).encode(bytes); - encode_u16_items(bytes, &(), &self.extension_data); + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { + (self.extension_type as u16).encode(bytes)?; + encode_u16_items(bytes, &(), &self.extension_data)?; + Ok(()) } } @@ -131,9 +134,10 @@ pub struct PlaintextInputShare { } impl Encode for PlaintextInputShare { - fn encode(&self, bytes: &mut Vec<u8>) { - encode_u16_items(bytes, &(), &self.extensions); - encode_u32_items(bytes, &(), &self.payload); + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { + encode_u16_items(bytes, &(), &self.extensions)?; + encode_u32_items(bytes, &(), &self.payload)?; + Ok(()) } } @@ -150,8 +154,9 @@ impl Decode for HpkeConfigId { } impl Encode for HpkeConfigId { - fn encode(&self, bytes: &mut Vec<u8>) { - self.0.encode(bytes); + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { + self.0.encode(bytes)?; + Ok(()) } } @@ -189,12 +194,13 @@ impl Decode for HpkeConfig { } impl Encode for HpkeConfig { - fn encode(&self, bytes: &mut Vec<u8>) { - self.id.encode(bytes); - self.kem_id.encode(bytes); - self.kdf_id.encode(bytes); - self.aead_id.encode(bytes); - encode_u16_items(bytes, &(), &self.public_key); + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { + self.id.encode(bytes)?; + self.kem_id.encode(bytes)?; + self.kdf_id.encode(bytes)?; + self.aead_id.encode(bytes)?; + encode_u16_items(bytes, &(), &self.public_key)?; + Ok(()) } } @@ -227,10 +233,11 @@ impl Decode for HpkeCiphertext { } impl Encode for HpkeCiphertext { - fn encode(&self, bytes: &mut Vec<u8>) { - self.config_id.encode(bytes); - encode_u16_items(bytes, &(), &self.enc); - encode_u32_items(bytes, &(), &self.payload); + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { + self.config_id.encode(bytes)?; + encode_u16_items(bytes, &(), &self.enc)?; + encode_u32_items(bytes, &(), &self.payload)?; + Ok(()) } } @@ -248,8 +255,9 @@ impl Decode for ReportID { } impl Encode for ReportID { - fn encode(&self, bytes: &mut Vec<u8>) { + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { bytes.extend_from_slice(&self.0); + Ok(()) } } @@ -286,9 +294,10 @@ impl Decode for ReportMetadata { } impl Encode for ReportMetadata { - fn encode(&self, bytes: &mut Vec<u8>) { - self.report_id.encode(bytes); - self.time.encode(bytes); + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { + self.report_id.encode(bytes)?; + self.time.encode(bytes)?; + Ok(()) } } @@ -307,7 +316,6 @@ pub struct Report { pub helper_encrypted_input_share: HpkeCiphertext, } - impl Report { /// Creates a minimal report for use in tests. pub fn new_dummy() -> Self { @@ -321,8 +329,16 @@ impl Report { time: Time::generate(1), }, public_share: vec![], - leader_encrypted_input_share: HpkeCiphertext { config_id: HpkeConfigId(5), enc: vec![1, 2, 3, 4, 5], payload: vec![6, 7, 8, 9, 10] }, - helper_encrypted_input_share: HpkeCiphertext { config_id: HpkeConfigId(100), enc: enc.into(), payload: payload.into() }, + leader_encrypted_input_share: HpkeCiphertext { + config_id: HpkeConfigId(5), + enc: vec![1, 2, 3, 4, 5], + payload: vec![6, 7, 8, 9, 10], + }, + helper_encrypted_input_share: HpkeCiphertext { + config_id: HpkeConfigId(100), + enc: enc.into(), + payload: payload.into(), + }, } } } @@ -349,10 +365,11 @@ impl Decode for Report { } impl Encode for Report { - fn encode(&self, bytes: &mut Vec<u8>) { - self.metadata.encode(bytes); - encode_u32_items(bytes, &(), &self.public_share); - self.leader_encrypted_input_share.encode(bytes); - self.helper_encrypted_input_share.encode(bytes); + fn encode(&self, bytes: &mut Vec<u8>) -> Result<(), CodecError> { + self.metadata.encode(bytes)?; + encode_u32_items(bytes, &(), &self.public_share)?; + self.leader_encrypted_input_share.encode(bytes)?; + self.helper_encrypted_input_share.encode(bytes)?; + Ok(()) } } diff --git a/toolkit/components/telemetry/dap/tests/xpcshell/test_dap.js b/toolkit/components/telemetry/dap/tests/xpcshell/test_dap.js index deb68bafef..ba973c647f 100644 --- a/toolkit/components/telemetry/dap/tests/xpcshell/test_dap.js +++ b/toolkit/components/telemetry/dap/tests/xpcshell/test_dap.js @@ -91,7 +91,7 @@ function uploadHandler(request, response) { console.log(body.available()); Assert.equal( true, - body.available() == 406 || body.available() == 3654, + body.available() == 886 || body.available() == 3654, "Wrong request body size." ); received = true; diff --git a/toolkit/components/telemetry/docs/data/environment.rst b/toolkit/components/telemetry/docs/data/environment.rst index 9e966fe707..6b0649bda7 100644 --- a/toolkit/components/telemetry/docs/data/environment.rst +++ b/toolkit/components/telemetry/docs/data/environment.rst @@ -104,6 +104,7 @@ Structure: creationDate: <integer>, // integer days since UNIX epoch, e.g. 16446 resetDate: <integer>, // integer days since UNIX epoch, e.g. 16446 - optional firstUseDate: <integer>, // integer days since UNIX epoch, e.g. 16446 - optional + recoveredFromBackup: <integer>, // integer days since UNIX epoch, e.g. 16446 - optional }, partner: { // This section may not be immediately available on startup distributionId: <string>, // pref "distribution.id", null on failure @@ -281,7 +282,7 @@ Structure: hasBinaryComponents: <bool>, installDay: <number>, // days since UNIX epoch, 0 on failure updateDay: <number>, // days since UNIX epoch, 0 on failure - signedState: <integer>, // whether the add-on is signed by AMO, only present for extensions + signedState: <integer>, // whether the add-on is signed by AMO signedTypes: <string>, // JSON-stringified array of signature types found (see nsIAppSignatureInfo's SignatureAlgorithm enum) isSystem: <bool>, // true if this is a System Add-on isWebExtension: <bool>, // true if this is a WebExtension @@ -302,6 +303,8 @@ Structure: hasBinaryComponents: <bool> installDay: <number>, // days since UNIX epoch, 0 on failure updateDay: <number>, // days since UNIX epoch, 0 on failure + signedState: <integer>, // whether the add-on is signed by AMO + signedTypes: <string>, // JSON-stringified array of signature types found (see nsIAppSignatureInfo's SignatureAlgorithm enum) }, activeGMPlugins: { <gmp id>: { @@ -547,6 +550,13 @@ The time of the first use of profile. If this is an old profile where we can't determine this this field will not be present. It's read from a file-stored timestamp from the client's profile directory. +recoveredFromBackup +~~~~~~~~~~~~~~~~~~~ + +The time that this profile was recovered from a backup. If the profile was never +recovered from a backup, this field will not be present. +It's read from a file-stored timestamp from the client's profile directory. + partner ------- diff --git a/toolkit/components/telemetry/docs/internals/preferences.rst b/toolkit/components/telemetry/docs/internals/preferences.rst index 0c63ae031d..1ce8647322 100644 --- a/toolkit/components/telemetry/docs/internals/preferences.rst +++ b/toolkit/components/telemetry/docs/internals/preferences.rst @@ -202,26 +202,6 @@ Data-choices notification This is the only channel-specific version that we currently use for the minimum policy version. -GeckoView ---------- - -``toolkit.telemetry.geckoview.streaming`` - - Whether the GeckoView mode we're running in is the variety that uses the :doc:`GeckoView Streaming Telemetry API <../internals/geckoview-streaming>` or not. - Defaults to false. - -``toolkit.telemetry.geckoview.batchDurationMS`` - - The duration in milliseconds over which :doc:`GeckoView Streaming Telemetry <../internals/geckoview-streaming>` will batch accumulations before passing it on to its delegate. - Defaults to 5000. - -``toolkit.telemetry.geckoview.maxBatchStalenessMS`` - - The maximum time (in milliseconds) between flushes of the - :doc:`GeckoView Streaming Telemetry <../internals/geckoview-streaming>` - batch to its delegate. - Defaults to 60000. - Testing ------- diff --git a/toolkit/components/telemetry/docs/internals/review.rst b/toolkit/components/telemetry/docs/internals/review.rst index 80a3bd57de..d38bcfcc3b 100644 --- a/toolkit/components/telemetry/docs/internals/review.rst +++ b/toolkit/components/telemetry/docs/internals/review.rst @@ -67,7 +67,7 @@ Considerations for all changes - Does this affect products more broadly than expected? - Consider all our platforms: Windows, Mac, Linux, Android. - - Consider all our products: Firefox, Fennec, GeckoView, Glean. + - Consider all our products: Firefox, Fenix, Glean. - Does this fall afoul of common architectural failures? diff --git a/toolkit/components/telemetry/geckoview/gtest/TestGeckoViewStreaming.cpp b/toolkit/components/telemetry/geckoview/gtest/TestGeckoViewStreaming.cpp deleted file mode 100644 index ebaa7099a8..0000000000 --- a/toolkit/components/telemetry/geckoview/gtest/TestGeckoViewStreaming.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/* vim:set ts=2 sw=2 sts=2 et: */ -/* Any copyright is dedicated to the Public Domain. - * http://creativecommons.org/publicdomain/zero/1.0/ - */ - -#include "gmock/gmock.h" -#include "gtest/gtest.h" -#include "mozilla/gtest/MozAssertions.h" -#include "mozilla/Preferences.h" -#include "mozilla/Services.h" -#include "mozilla/Telemetry.h" -#include "nsIObserverService.h" -#include "nsThreadUtils.h" -#include "TelemetryFixture.h" -#include "TelemetryTestHelpers.h" -#include "streaming/GeckoViewStreamingTelemetry.h" - -using namespace mozilla; -using namespace mozilla::Telemetry; -using namespace TelemetryTestHelpers; -using GeckoViewStreamingTelemetry::StreamingTelemetryDelegate; -using mozilla::Telemetry::ScalarID; -using ::testing::_; -using ::testing::Eq; -using ::testing::StrictMock; - -namespace { - -const char* kGeckoViewStreamingPref = "toolkit.telemetry.geckoview.streaming"; -const char* kBatchTimeoutPref = "toolkit.telemetry.geckoview.batchDurationMS"; - -constexpr auto kTestHgramName = "TELEMETRY_TEST_STREAMING"_ns; -constexpr auto kTestHgramName2 = "TELEMETRY_TEST_STREAMING_2"_ns; -constexpr auto kTestCategoricalName = "TELEMETRY_TEST_CATEGORICAL_OPTOUT"_ns; -const HistogramID kTestHgram = Telemetry::TELEMETRY_TEST_STREAMING; -const HistogramID kTestHgram2 = Telemetry::TELEMETRY_TEST_STREAMING_2; - -class TelemetryStreamingFixture : public TelemetryTestFixture { - protected: - virtual void SetUp() { - TelemetryTestFixture::SetUp(); - Preferences::SetBool(kGeckoViewStreamingPref, true); - Preferences::SetInt(kBatchTimeoutPref, 5000); - } - virtual void TearDown() { - TelemetryTestFixture::TearDown(); - Preferences::SetBool(kGeckoViewStreamingPref, false); - GeckoViewStreamingTelemetry::RegisterDelegate(nullptr); - } -}; - -class MockDelegate : public StreamingTelemetryDelegate { - public: - MOCK_METHOD2(ReceiveHistogramSamples, - void(const nsCString& aHistogramName, - const nsTArray<uint32_t>& aSamples)); - MOCK_METHOD2(ReceiveCategoricalHistogramSamples, - void(const nsCString& aHistogramName, - const nsTArray<uint32_t>& aSamples)); - MOCK_METHOD2(ReceiveBoolScalarValue, - void(const nsCString& aScalarName, bool aValue)); - MOCK_METHOD2(ReceiveStringScalarValue, - void(const nsCString& aScalarName, const nsCString& aValue)); - MOCK_METHOD2(ReceiveUintScalarValue, - void(const nsCString& aScalarName, uint32_t aValue)); -}; // class MockDelegate - -TEST_F(TelemetryStreamingFixture, HistogramSamples) { - const uint32_t kSampleOne = 401; - const uint32_t kSampleTwo = 2019; - - CopyableTArray<uint32_t> samplesArray; - samplesArray.AppendElement(kSampleOne); - samplesArray.AppendElement(kSampleTwo); - - auto md = MakeRefPtr<MockDelegate>(); - EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), - Eq(std::move(samplesArray)))); - GeckoViewStreamingTelemetry::RegisterDelegate(md); - - Telemetry::Accumulate(Telemetry::TELEMETRY_TEST_STREAMING, kSampleOne); - Preferences::SetInt(kBatchTimeoutPref, 0); - Telemetry::Accumulate(Telemetry::TELEMETRY_TEST_STREAMING, kSampleTwo); -} - -TEST_F(TelemetryStreamingFixture, CategoricalHistogramSamples) { - auto kSampleOne = - Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL_OPTOUT::CommonLabel; - auto kSampleTwo = Telemetry::LABELS_TELEMETRY_TEST_CATEGORICAL_OPTOUT::Label5; - - CopyableTArray<uint32_t> samplesArray; - samplesArray.AppendElement(static_cast<uint32_t>(kSampleOne)); - samplesArray.AppendElement(static_cast<uint32_t>(kSampleOne)); - samplesArray.AppendElement(static_cast<uint32_t>(kSampleTwo)); - - auto md = MakeRefPtr<MockDelegate>(); - EXPECT_CALL(*md, ReceiveCategoricalHistogramSamples( - Eq(kTestCategoricalName), Eq(std::move(samplesArray)))); - GeckoViewStreamingTelemetry::RegisterDelegate(md); - - Telemetry::AccumulateCategorical(kSampleOne); - Telemetry::AccumulateCategorical(kSampleOne); - Preferences::SetInt(kBatchTimeoutPref, 0); - Telemetry::AccumulateCategorical(kSampleTwo); -} - -TEST_F(TelemetryStreamingFixture, MultipleHistograms) { - const uint32_t kSample1 = 400; - const uint32_t kSample2 = 1 << 31; - const uint32_t kSample3 = 7; - CopyableTArray<uint32_t> samplesArray1; - samplesArray1.AppendElement(kSample1); - samplesArray1.AppendElement(kSample2); - CopyableTArray<uint32_t> samplesArray2; - samplesArray2.AppendElement(kSample3); - - auto md = MakeRefPtr<MockDelegate>(); - EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), - Eq(std::move(samplesArray1)))); - EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName2), - Eq(std::move(samplesArray2)))); - - GeckoViewStreamingTelemetry::RegisterDelegate(md); - - Telemetry::Accumulate(kTestHgram, kSample1); - Telemetry::Accumulate(kTestHgram2, kSample3); - Preferences::SetInt(kBatchTimeoutPref, 0); - Telemetry::Accumulate(kTestHgram, kSample2); -} - -// If we can find a way to convert the expectation's arg into an stl container, -// we can use gmock's own ::testing::UnorderedElementsAre() instead. -auto MatchUnordered(uint32_t sample1, uint32_t sample2) { - CopyableTArray<uint32_t> samplesArray1; - samplesArray1.AppendElement(sample1); - samplesArray1.AppendElement(sample2); - - CopyableTArray<uint32_t> samplesArray2; - samplesArray2.AppendElement(sample2); - samplesArray2.AppendElement(sample1); - - return ::testing::AnyOf(Eq(std::move(samplesArray1)), - Eq(std::move(samplesArray2))); -} - -TEST_F(TelemetryStreamingFixture, MultipleThreads) { - const uint32_t kSample1 = 4; - const uint32_t kSample2 = 14; - - auto md = MakeRefPtr<MockDelegate>(); - // In this test, samples for the second test hgram are uninteresting. - EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName2), _)); - EXPECT_CALL(*md, ReceiveHistogramSamples(Eq(kTestHgramName), - MatchUnordered(kSample1, kSample2))); - - GeckoViewStreamingTelemetry::RegisterDelegate(md); - - nsCOMPtr<nsIThread> t1; - nsCOMPtr<nsIThread> t2; - nsCOMPtr<nsIThread> t3; - - nsCOMPtr<nsIRunnable> r1 = NS_NewRunnableFunction( - "accumulate 4", [&]() { Telemetry::Accumulate(kTestHgram, kSample1); }); - nsCOMPtr<nsIRunnable> r2 = NS_NewRunnableFunction( - "accumulate 14", [&]() { Telemetry::Accumulate(kTestHgram, kSample2); }); - - nsresult rv = NS_NewNamedThread("t1", getter_AddRefs(t1), r1); - EXPECT_NS_SUCCEEDED(rv); - rv = NS_NewNamedThread("t2", getter_AddRefs(t2), r2); - EXPECT_NS_SUCCEEDED(rv); - - // Give the threads a chance to do their work. - PR_Sleep(PR_MillisecondsToInterval(1)); - - Preferences::SetInt(kBatchTimeoutPref, 0); - Telemetry::Accumulate(kTestHgram2, kSample1); -} - -TEST_F(TelemetryStreamingFixture, ScalarValues) { - constexpr auto kBoolScalarName = "telemetry.test.boolean_kind"_ns; - constexpr auto kStringScalarName = "telemetry.test.string_kind"_ns; - constexpr auto kUintScalarName = "telemetry.test.unsigned_int_kind"_ns; - - const bool kBoolScalarValue = true; - constexpr auto kStringScalarValue = "a string scalar value"_ns; - const uint32_t kUintScalarValue = 42; - - auto md = MakeRefPtr<MockDelegate>(); - EXPECT_CALL( - *md, ReceiveBoolScalarValue(Eq(kBoolScalarName), Eq(kBoolScalarValue))); - EXPECT_CALL(*md, ReceiveStringScalarValue(Eq(kStringScalarName), - Eq(kStringScalarValue))); - EXPECT_CALL( - *md, ReceiveUintScalarValue(Eq(kUintScalarName), Eq(kUintScalarValue))); - - GeckoViewStreamingTelemetry::RegisterDelegate(md); - - Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, kBoolScalarValue); - Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_STRING_KIND, - NS_ConvertUTF8toUTF16(kStringScalarValue)); - Preferences::SetInt(kBatchTimeoutPref, - 0); // Trigger batch on next accumulation. - Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, - kUintScalarValue); -} - -TEST_F(TelemetryStreamingFixture, ExpiredHistogram) { - const HistogramID kExpiredHistogram = Telemetry::TELEMETRY_TEST_EXPIRED; - const uint32_t kSample = 401; - - // Strict Mock fails on any method calls. - auto md = MakeRefPtr<StrictMock<MockDelegate>>(); - GeckoViewStreamingTelemetry::RegisterDelegate(md); - - Preferences::SetInt(kBatchTimeoutPref, 0); - Telemetry::Accumulate(kExpiredHistogram, kSample); -} - -TEST_F(TelemetryStreamingFixture, SendOnAppBackground) { - constexpr auto kBoolScalarName = "telemetry.test.boolean_kind"_ns; - const bool kBoolScalarValue = true; - const char* kApplicationBackgroundTopic = "application-background"; - - auto md = MakeRefPtr<MockDelegate>(); - EXPECT_CALL( - *md, ReceiveBoolScalarValue(Eq(kBoolScalarName), Eq(kBoolScalarValue))); - - GeckoViewStreamingTelemetry::RegisterDelegate(md); - Telemetry::ScalarSet(ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, kBoolScalarValue); - - nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); - ASSERT_TRUE(!!os) - << "Observer Service unavailable?!?!"; - os->NotifyObservers(nullptr, kApplicationBackgroundTopic, nullptr); -} - -} // namespace diff --git a/toolkit/components/telemetry/geckoview/gtest/moz.build b/toolkit/components/telemetry/geckoview/gtest/moz.build deleted file mode 100644 index eb6a2f9293..0000000000 --- a/toolkit/components/telemetry/geckoview/gtest/moz.build +++ /dev/null @@ -1,28 +0,0 @@ -# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, you can obtain one at http://mozilla.org/MPL/2.0/. - -Library("telemetrygeckoviewtest") - -LOCAL_INCLUDES += [ - "../", - "../..", - "../../..", - "/toolkit/components/telemetry/tests/gtest", - "/xpcom/io", -] - -# GeckoView Streaming Telemetry is only available on Android. -if CONFIG["MOZ_WIDGET_TOOLKIT"] == "android": - UNIFIED_SOURCES += [ - "TestGeckoViewStreaming.cpp", - ] - -# We need the following line otherwise including -# "TelemetryHistogram.h" in tests will fail due to -# missing headers. -include("/ipc/chromium/chromium-config.mozbuild") - -FINAL_LIBRARY = "xul-gtest" diff --git a/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.cpp b/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.cpp deleted file mode 100644 index 6c4b9590c0..0000000000 --- a/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.cpp +++ /dev/null @@ -1,282 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "GeckoViewStreamingTelemetry.h" - -#include "mozilla/Assertions.h" -#include "mozilla/Services.h" -#include "mozilla/StaticMutex.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/StaticPrefs_toolkit.h" -#include "mozilla/TimeStamp.h" -#include "nsTHashMap.h" -#include "nsIObserver.h" -#include "nsIObserverService.h" -#include "nsITimer.h" -#include "nsTArray.h" -#include "nsThreadUtils.h" - -using mozilla::Runnable; -using mozilla::StaticMutex; -using mozilla::StaticMutexAutoLock; -using mozilla::StaticRefPtr; -using mozilla::TimeStamp; - -// Batches and streams Telemetry samples to a JNI delegate which will -// (presumably) do something with the data. Expected to be used to route data -// up to the Android Components layer to be translated into Glean metrics. -namespace GeckoViewStreamingTelemetry { - -class LifecycleObserver; -void SendBatch(const StaticMutexAutoLock& aLock); - -// Topic on which we flush the batch. -static const char* const kApplicationBackgroundTopic = "application-background"; - -static StaticMutex gMutex MOZ_UNANNOTATED; - -// -- The following state is accessed across threads. -// -- Do not touch these if you do not hold gMutex. - -// The time the batch began. -TimeStamp gBatchBegan; -// The batch of histograms and samples. -typedef nsTHashMap<nsCStringHashKey, nsTArray<uint32_t>> HistogramBatch; -HistogramBatch gBatch; -HistogramBatch gCategoricalBatch; -// The batches of Scalars and their values. -typedef nsTHashMap<nsCStringHashKey, bool> BoolScalarBatch; -BoolScalarBatch gBoolScalars; -typedef nsTHashMap<nsCStringHashKey, nsCString> StringScalarBatch; -StringScalarBatch gStringScalars; -typedef nsTHashMap<nsCStringHashKey, uint32_t> UintScalarBatch; -UintScalarBatch gUintScalars; -// The delegate to receive the samples and values. -StaticRefPtr<StreamingTelemetryDelegate> gDelegate; -// Lifecycle observer used to flush the batch when backgrounded. -StaticRefPtr<LifecycleObserver> gObserver; - -// -- End of gMutex-protected thread-unsafe-accessed data - -// Timer that ensures data in the batch never gets too stale. -// This timer may only be manipulated on the Main Thread. -StaticRefPtr<nsITimer> gJICTimer; - -class LifecycleObserver final : public nsIObserver { - public: - NS_DECL_ISUPPORTS - NS_DECL_NSIOBSERVER - - LifecycleObserver() = default; - - protected: - ~LifecycleObserver() = default; -}; - -NS_IMPL_ISUPPORTS(LifecycleObserver, nsIObserver); - -NS_IMETHODIMP -LifecycleObserver::Observe(nsISupports* aSubject, const char* aTopic, - const char16_t* aData) { - if (!strcmp(aTopic, kApplicationBackgroundTopic)) { - StaticMutexAutoLock lock(gMutex); - SendBatch(lock); - } - return NS_OK; -} - -void RegisterDelegate(const RefPtr<StreamingTelemetryDelegate>& aDelegate) { - StaticMutexAutoLock lock(gMutex); - gDelegate = aDelegate; -} - -class SendBatchRunnable : public Runnable { - public: - explicit SendBatchRunnable(RefPtr<StreamingTelemetryDelegate> aDelegate, - HistogramBatch&& aBatch, - HistogramBatch&& aCategoricalBatch, - BoolScalarBatch&& aBoolScalars, - StringScalarBatch&& aStringScalars, - UintScalarBatch&& aUintScalars) - : Runnable("SendBatchRunnable"), - mDelegate(std::move(aDelegate)), - mBatch(std::move(aBatch)), - mCategoricalBatch(std::move(aCategoricalBatch)), - mBoolScalars(std::move(aBoolScalars)), - mStringScalars(std::move(aStringScalars)), - mUintScalars(std::move(aUintScalars)) {} - - NS_IMETHOD Run() override { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(mDelegate); - - if (gJICTimer) { - gJICTimer->Cancel(); - } - - for (const auto& entry : mBatch) { - const nsCString& histogramName = PromiseFlatCString(entry.GetKey()); - const nsTArray<uint32_t>& samples = entry.GetData(); - - mDelegate->ReceiveHistogramSamples(histogramName, samples); - } - mBatch.Clear(); - - for (const auto& entry : mCategoricalBatch) { - const nsCString& histogramName = PromiseFlatCString(entry.GetKey()); - const nsTArray<uint32_t>& samples = entry.GetData(); - - mDelegate->ReceiveCategoricalHistogramSamples(histogramName, samples); - } - mCategoricalBatch.Clear(); - - for (const auto& entry : mBoolScalars) { - const nsCString& scalarName = PromiseFlatCString(entry.GetKey()); - mDelegate->ReceiveBoolScalarValue(scalarName, entry.GetData()); - } - mBoolScalars.Clear(); - - for (const auto& entry : mStringScalars) { - const nsCString& scalarName = PromiseFlatCString(entry.GetKey()); - const nsCString& scalarValue = PromiseFlatCString(entry.GetData()); - mDelegate->ReceiveStringScalarValue(scalarName, scalarValue); - } - mStringScalars.Clear(); - - for (const auto& entry : mUintScalars) { - const nsCString& scalarName = PromiseFlatCString(entry.GetKey()); - mDelegate->ReceiveUintScalarValue(scalarName, entry.GetData()); - } - mUintScalars.Clear(); - - return NS_OK; - } - - private: - RefPtr<StreamingTelemetryDelegate> mDelegate; - HistogramBatch mBatch; - HistogramBatch mCategoricalBatch; - BoolScalarBatch mBoolScalars; - StringScalarBatch mStringScalars; - UintScalarBatch mUintScalars; -}; // class SendBatchRunnable - -// Can be called on any thread. -// NOTE: Pay special attention to what you call in this method as if it -// accumulates to a gv-streaming-enabled probe we will deadlock the calling -// thread. -void SendBatch(const StaticMutexAutoLock& aLock) { - if (!gDelegate) { - NS_WARNING( - "Being asked to send Streaming Telemetry with no registered Streaming " - "Telemetry Delegate. Will try again later."); - // Give us another full Batch Duration to register a delegate. - gBatchBegan = TimeStamp::Now(); - return; - } - - // To make it so accumulations within the delegation don't deadlock us, - // move the batches' contents into the Runner. - HistogramBatch histogramCopy; - gBatch.SwapElements(histogramCopy); - HistogramBatch categoricalCopy; - gCategoricalBatch.SwapElements(categoricalCopy); - BoolScalarBatch boolScalarCopy; - gBoolScalars.SwapElements(boolScalarCopy); - StringScalarBatch stringScalarCopy; - gStringScalars.SwapElements(stringScalarCopy); - UintScalarBatch uintScalarCopy; - gUintScalars.SwapElements(uintScalarCopy); - RefPtr<SendBatchRunnable> runnable = new SendBatchRunnable( - gDelegate, std::move(histogramCopy), std::move(categoricalCopy), - std::move(boolScalarCopy), std::move(stringScalarCopy), - std::move(uintScalarCopy)); - - // To make things easier for the delegate, dispatch to the main thread. - NS_DispatchToMainThread(runnable); -} - -// Can be called on any thread. -void BatchCheck(const StaticMutexAutoLock& aLock) { - if (!gObserver) { - gObserver = new LifecycleObserver(); - nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); - if (os) { - os->AddObserver(gObserver, kApplicationBackgroundTopic, false); - } - } - if (gBatchBegan.IsNull()) { - // Time to begin a new batch. - gBatchBegan = TimeStamp::Now(); - // Set a just-in-case timer to enforce an upper-bound on batch staleness. - NS_DispatchToMainThread(NS_NewRunnableFunction( - "GeckoviewStreamingTelemetry::ArmTimer", []() -> void { - if (!gJICTimer) { - gJICTimer = NS_NewTimer().take(); - } - if (gJICTimer) { - gJICTimer->InitWithNamedFuncCallback( - [](nsITimer*, void*) -> void { - StaticMutexAutoLock locker(gMutex); - SendBatch(locker); - }, - nullptr, - mozilla::StaticPrefs:: - toolkit_telemetry_geckoview_maxBatchStalenessMS(), - nsITimer::TYPE_ONE_SHOT_LOW_PRIORITY, - "GeckoviewStreamingTelemetry::SendBatch"); - } - })); - } - double batchDurationMs = (TimeStamp::Now() - gBatchBegan).ToMilliseconds(); - if (batchDurationMs > - mozilla::StaticPrefs::toolkit_telemetry_geckoview_batchDurationMS()) { - SendBatch(aLock); - gBatchBegan = TimeStamp(); - } -} - -// Can be called on any thread. -void HistogramAccumulate(const nsCString& aName, bool aIsCategorical, - uint32_t aValue) { - StaticMutexAutoLock lock(gMutex); - - if (aIsCategorical) { - nsTArray<uint32_t>& samples = gCategoricalBatch.LookupOrInsert(aName); - samples.AppendElement(aValue); - } else { - nsTArray<uint32_t>& samples = gBatch.LookupOrInsert(aName); - samples.AppendElement(aValue); - } - - BatchCheck(lock); -} - -void BoolScalarSet(const nsCString& aName, bool aValue) { - StaticMutexAutoLock lock(gMutex); - - gBoolScalars.InsertOrUpdate(aName, aValue); - - BatchCheck(lock); -} - -void StringScalarSet(const nsCString& aName, const nsCString& aValue) { - StaticMutexAutoLock lock(gMutex); - - gStringScalars.InsertOrUpdate(aName, aValue); - - BatchCheck(lock); -} - -void UintScalarSet(const nsCString& aName, uint32_t aValue) { - StaticMutexAutoLock lock(gMutex); - - gUintScalars.InsertOrUpdate(aName, aValue); - - BatchCheck(lock); -} - -} // namespace GeckoViewStreamingTelemetry diff --git a/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.h b/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.h deleted file mode 100644 index 458224a3c2..0000000000 --- a/toolkit/components/telemetry/geckoview/streaming/GeckoViewStreamingTelemetry.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef GeckoViewStreamingTelemetry_h__ -#define GeckoViewStreamingTelemetry_h__ - -#include "mozilla/Assertions.h" -#include "mozilla/RefPtr.h" -#include "nsISupports.h" -#include "nsStringFwd.h" -#include "nsTArray.h" - -#include <cstdint> - -namespace GeckoViewStreamingTelemetry { - -void HistogramAccumulate(const nsCString& aName, bool aIsCategorical, - uint32_t aValue); - -void BoolScalarSet(const nsCString& aName, bool aValue); -void StringScalarSet(const nsCString& aName, const nsCString& aValue); -void UintScalarSet(const nsCString& aName, uint32_t aValue); - -// Classes wishing to receive Streaming Telemetry must implement this interface -// and register themselves via RegisterDelegate. -class StreamingTelemetryDelegate { - public: - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(StreamingTelemetryDelegate) - - // Receive* methods will be called from time to time on the main thread. - virtual void ReceiveHistogramSamples(const nsCString& aName, - const nsTArray<uint32_t>& aSamples) = 0; - virtual void ReceiveCategoricalHistogramSamples( - const nsCString& aName, const nsTArray<uint32_t>& aSamples) = 0; - virtual void ReceiveBoolScalarValue(const nsCString& aName, bool aValue) = 0; - virtual void ReceiveStringScalarValue(const nsCString& aName, - const nsCString& aValue) = 0; - virtual void ReceiveUintScalarValue(const nsCString& aName, - uint32_t aValue) = 0; - - protected: - virtual ~StreamingTelemetryDelegate() = default; -}; - -// Registers the provided StreamingTelemetryDelegate to receive Streaming -// Telemetry, overwriting any previous delegate registration. -// Call on any thread. -void RegisterDelegate(const RefPtr<StreamingTelemetryDelegate>& aDelegate); - -} // namespace GeckoViewStreamingTelemetry - -#endif // GeckoViewStreamingTelemetry_h__ diff --git a/toolkit/components/telemetry/geckoview/streaming/metrics.yaml b/toolkit/components/telemetry/geckoview/streaming/metrics.yaml deleted file mode 100644 index 2386fb2171..0000000000 --- a/toolkit/components/telemetry/geckoview/streaming/metrics.yaml +++ /dev/null @@ -1,13 +0,0 @@ -# This Source Code Form is subject to the terms of the Mozilla Public -# License, v. 2.0. If a copy of the MPL was not distributed with this -# file, You can obtain one at http://mozilla.org/MPL/2.0/. - -# This file defines the metrics that are recorded by the Glean SDK. They are -# automatically converted to platform-specific code at build time using the -# `glean_parser` PyPI package. - -# Adding a new metric? Please don't! -# (At least not without the permission of a Telemetry Module Peer) - ---- -$schema: moz://mozilla.org/schemas/glean/metrics/2-0-0 diff --git a/toolkit/components/telemetry/moz.build b/toolkit/components/telemetry/moz.build index 718120cf78..6da23fa372 100644 --- a/toolkit/components/telemetry/moz.build +++ b/toolkit/components/telemetry/moz.build @@ -30,9 +30,7 @@ with Files("docs/**"): SCHEDULES.exclusive = ["docs"] if CONFIG["ENABLE_TESTS"]: - # We used to need GeckoView tests as a separate directory. This - # is no longer true and we could probably move it to tests/gtest. - DIRS += ["geckoview/gtest", "tests/gtest"] + DIRS += ["tests/gtest"] TEST_DIRS += ["tests", "dap/ffi-gtest"] @@ -78,7 +76,6 @@ SOURCES += [ "core/TelemetryScalar.cpp", "core/TelemetryUserInteraction.cpp", "dap/DAPTelemetry.cpp", - "geckoview/streaming/GeckoViewStreamingTelemetry.cpp", "other/CombinedStacks.cpp", "other/ProcessedStack.cpp", "other/TelemetryIOInterposeObserver.cpp", @@ -244,18 +241,6 @@ GeneratedFile( inputs=processes_files, ) -# Ensure that the GeckoView metrics file parses correctly prior to it -# being released in Android components. This triggers glean_parser by -# pretending to generate a file named 'glean_checks'. -# While this currently only applies to Android, in the medium-term it -# is going to generate code for Firefox as well (project FOG). -# Prior art for this was in bug 1063728, within SpiderMonkey tests. -GeneratedFile( - "glean_checks", - script="build_scripts/run_glean_parser.py", - inputs=["geckoview/streaming/metrics.yaml"], -) - # Generate UserInteraction file userinteraction_files = [ "UserInteractions.yaml", diff --git a/toolkit/components/telemetry/tests/gtest/TestScalars.cpp b/toolkit/components/telemetry/tests/gtest/TestScalars.cpp index 201641e77a..3828cbdcd8 100644 --- a/toolkit/components/telemetry/tests/gtest/TestScalars.cpp +++ b/toolkit/components/telemetry/tests/gtest/TestScalars.cpp @@ -395,68 +395,6 @@ TEST_F(TelemetryTestFixture, ScalarEventSummary_Dynamic) { scalarsSnapshot, 2); } -TEST_F(TelemetryTestFixture, WrongScalarOperator) { - AutoJSContextWithGlobal cx(mCleanGlobal); - - // Make sure we don't get scalars from other tests. - Unused << mTelemetry->ClearScalars(); - - const uint32_t expectedValue = 1172015; - - Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, - expectedValue); - Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, - NS_LITERAL_STRING_FROM_CSTRING(EXPECTED_STRING)); - Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, true); - - TelemetryScalar::DeserializationStarted(); - - Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_STRING_KIND, 1447); - Telemetry::ScalarAdd(Telemetry::ScalarID::TELEMETRY_TEST_BOOLEAN_KIND, 1447); - Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_UNSIGNED_INT_KIND, - true); - TelemetryScalar::ApplyPendingOperations(); - - JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext()); - GetScalarsSnapshot(false, cx.GetJSContext(), &scalarsSnapshot); - CheckStringScalar("telemetry.test.string_kind", cx.GetJSContext(), - scalarsSnapshot, EXPECTED_STRING); - CheckBoolScalar("telemetry.test.boolean_kind", cx.GetJSContext(), - scalarsSnapshot, true); - CheckUintScalar("telemetry.test.unsigned_int_kind", cx.GetJSContext(), - scalarsSnapshot, expectedValue); -} - -TEST_F(TelemetryTestFixture, WrongKeyedScalarOperator) { - AutoJSContextWithGlobal cx(mCleanGlobal); - - // Make sure we don't get scalars from other tests. - Unused << mTelemetry->ClearScalars(); - - const uint32_t kExpectedUint = 1172017; - - Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT, - u"key1"_ns, kExpectedUint); - Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND, - u"key2"_ns, true); - - TelemetryScalar::DeserializationStarted(); - - Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_UNSIGNED_INT, - u"key1"_ns, false); - Telemetry::ScalarSet(Telemetry::ScalarID::TELEMETRY_TEST_KEYED_BOOLEAN_KIND, - u"key2"_ns, static_cast<uint32_t>(13)); - - TelemetryScalar::ApplyPendingOperations(); - - JS::Rooted<JS::Value> scalarsSnapshot(cx.GetJSContext()); - GetScalarsSnapshot(true, cx.GetJSContext(), &scalarsSnapshot); - CheckKeyedUintScalar("telemetry.test.keyed_unsigned_int", "key1", - cx.GetJSContext(), scalarsSnapshot, kExpectedUint); - CheckKeyedBoolScalar("telemetry.test.keyed_boolean_kind", "key2", - cx.GetJSContext(), scalarsSnapshot, true); -} - TEST_F(TelemetryTestFixture, TestKeyedScalarAllowedKeys) { AutoJSContextWithGlobal cx(mCleanGlobal); // Make sure we don't get scalars from other tests. diff --git a/toolkit/components/telemetry/tests/python/test_histogramtools_strict.py b/toolkit/components/telemetry/tests/python/test_histogramtools_strict.py index 2109cd7d35..28d4646dfd 100644 --- a/toolkit/components/telemetry/tests/python/test_histogramtools_strict.py +++ b/toolkit/components/telemetry/tests/python/test_histogramtools_strict.py @@ -485,53 +485,6 @@ class TestParser(unittest.TestCase): ) self.assertRaises(SystemExit, ParserError.exit_func) - def test_gv_streaming_unsupported_kind(self): - SAMPLE_HISTOGRAM = { - "TEST_HISTOGRAM_GV_STREAMING": { - "record_in_processes": ["main", "content"], - "alert_emails": ["team@mozilla.xyz"], - "bug_numbers": [1383793], - "expires_in_version": "never", - "kind": "boolean", - "description": "Test histogram", - "products": ["geckoview_streaming"], - } - } - histograms = load_histogram(SAMPLE_HISTOGRAM) - parse_histograms.load_allowlist() - parse_histograms.Histogram( - "TEST_HISTOGRAM_GV_STREAMING", - histograms["TEST_HISTOGRAM_GV_STREAMING"], - strict_type_checks=True, - ) - self.assertRaises(SystemExit, ParserError.exit_func) - - def test_gv_streaming_keyed(self): - SAMPLE_HISTOGRAM = { - "TEST_HISTOGRAM_GV_STREAMING": { - "record_in_processes": ["main", "content"], - "alert_emails": ["team@mozilla.xyz"], - "bug_numbers": [1383793], - "expires_in_version": "never", - "kind": "exponential", - "low": 1024, - "high": 2**64, - "n_buckets": 100, - "keyed": "true", - "description": "Test histogram", - "products": ["geckoview_streaming"], - } - } - histograms = load_histogram(SAMPLE_HISTOGRAM) - parse_histograms.load_allowlist() - parse_histograms.Histogram( - "TEST_HISTOGRAM_GV_STREAMING", - histograms["TEST_HISTOGRAM_GV_STREAMING"], - strict_type_checks=True, - ) - - self.assertRaises(SystemExit, ParserError.exit_func) - def test_enumerated_histogram_with_100_buckets(self): SAMPLE_HISTOGRAM = { "TEST_100_BUCKETS_HISTOGRAM": { diff --git a/toolkit/components/telemetry/tests/python/test_parse_events.py b/toolkit/components/telemetry/tests/python/test_parse_events.py index 0b7b91efcc..d6c7859eee 100644 --- a/toolkit/components/telemetry/tests/python/test_parse_events.py +++ b/toolkit/components/telemetry/tests/python/test_parse_events.py @@ -145,22 +145,6 @@ expiry_version: never ), ) - def test_geckoview_streaming_product(self): - SAMPLE_EVENT = """ -methods: ["method1", "method2"] -objects: ["object1", "object2"] -bug_numbers: [12345] -notification_emails: ["test01@mozilla.com", "test02@mozilla.com"] -record_in_processes: ["content"] -description: This is a test entry for Telemetry. -products: ["geckoview_streaming"] -expiry_version: never -""" - event = load_event(SAMPLE_EVENT) - parse_events.EventData("CATEGORY", "test_event", event, strict_type_checks=True) - - self.assertRaises(SystemExit, ParserError.exit_func) - if __name__ == "__main__": mozunit.main() diff --git a/toolkit/components/telemetry/tests/python/test_parse_scalars.py b/toolkit/components/telemetry/tests/python/test_parse_scalars.py index c699cdb4d8..315c920a4a 100644 --- a/toolkit/components/telemetry/tests/python/test_parse_scalars.py +++ b/toolkit/components/telemetry/tests/python/test_parse_scalars.py @@ -243,25 +243,6 @@ bug_numbers: parse_scalars.ScalarType("CATEGORY", "PROVE", scalar, strict_type_checks=True) self.assertRaises(SystemExit, ParserError.exit_func) - def test_gv_streaming_keyed(self): - SAMPLE_SCALAR = """ -description: A nice one-line description. -expires: never -record_in_processes: - - 'main' -kind: uint -notification_emails: - - test01@mozilla.com -products: ['geckoview_streaming'] -keyed: true -bug_numbers: - - 12345 -""" - - scalar = load_scalar(SAMPLE_SCALAR) - parse_scalars.ScalarType("CATEGORY", "PROVE", scalar, strict_type_checks=True) - self.assertRaises(SystemExit, ParserError.exit_func) - if __name__ == "__main__": mozunit.main() diff --git a/toolkit/components/telemetry/tests/unit/TelemetryEnvironmentTesting.sys.mjs b/toolkit/components/telemetry/tests/unit/TelemetryEnvironmentTesting.sys.mjs index 0df6dfc2c1..1a4015df36 100644 --- a/toolkit/components/telemetry/tests/unit/TelemetryEnvironmentTesting.sys.mjs +++ b/toolkit/components/telemetry/tests/unit/TelemetryEnvironmentTesting.sys.mjs @@ -40,6 +40,8 @@ const PROFILE_RESET_DATE_MS = Date.now(); // The profile creation date, in milliseconds (Yesterday). const PROFILE_FIRST_USE_MS = PROFILE_RESET_DATE_MS - MILLISECONDS_PER_DAY; const PROFILE_CREATION_DATE_MS = PROFILE_FIRST_USE_MS - MILLISECONDS_PER_DAY; +const PROFILE_RECOVERED_FROM_BACKUP = + PROFILE_RESET_DATE_MS - MILLISECONDS_PER_HOUR; const GFX_VENDOR_ID = "0xabcd"; const GFX_DEVICE_ID = "0x1234"; @@ -126,6 +128,7 @@ export var TelemetryEnvironmentTesting = { created: PROFILE_CREATION_DATE_MS, reset: PROFILE_RESET_DATE_MS, firstUse: PROFILE_FIRST_USE_MS, + recoveredFromBackup: PROFILE_RECOVERED_FROM_BACKUP, } ); }, @@ -392,6 +395,10 @@ export var TelemetryEnvironmentTesting = { data.profile.firstUseDate, truncateToDays(PROFILE_FIRST_USE_MS) ); + lazy.Assert.equal( + data.profile.recoveredFromBackup, + truncateToDays(PROFILE_RECOVERED_FROM_BACKUP) + ); }, checkPartnerSection(data, isInitial) { diff --git a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js index 9990d83fdf..ca43772a07 100644 --- a/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js +++ b/toolkit/components/telemetry/tests/unit/test_TelemetryEnvironment.js @@ -712,6 +712,64 @@ add_task(async function test_addons() { await addon.uninstall(); }); +add_task(async function test_signedTheme() { + AddonTestUtils.useRealCertChecks = true; + + const { PKCS7_WITH_SHA1, COSE_WITH_SHA256 } = Ci.nsIAppSignatureInfo; + + const ADDON_THEME_INSTALL_URL = gDataRoot + "webext-implicit-id.xpi"; + const ADDON_THEME_ID = "{46607a7b-1b2a-40ce-9afe-91cda52c46a6}"; + + // Install the theme. + let deferred = Promise.withResolvers(); + TelemetryEnvironment.registerChangeListener( + "test_signedAddon", + deferred.resolve + ); + let theme = await installXPIFromURL(ADDON_THEME_INSTALL_URL); + await theme.enable(); + ok(theme.isActive, "Theme should be active"); + + // Install an extension to force the telemetry environment to be + // updated (currently theme add-ons changes do not seem to be + // notified as changes, see EnvironmentAddonBuilder _updateAddons + // method for how changes to the environment.addons property are + // being detected). + const ADDON_INSTALL_URL = gDataRoot + "amosigned.xpi"; + let addon = await installXPIFromURL(ADDON_INSTALL_URL); + + await deferred.promise; + TelemetryEnvironment.unregisterChangeListener("test_signedAddon"); + + let data = TelemetryEnvironment.currentEnvironment; + TelemetryEnvironmentTesting.checkEnvironmentData(data); + + // Check signedState and signedTypes on active theme data + // (NOTE: other properties of active theme are technically + // not covered by any other test task in this xpcshell test). + Assert.equal( + data.addons.theme.id, + ADDON_THEME_ID, + "Theme should be in the environment." + ); + Assert.equal( + data.addons.theme.signedState, + AddonManager.SIGNEDSTATE_SIGNED, + "Got expected signedState on activeTheme" + ); + Assert.equal( + data.addons.theme.signedTypes, + JSON.stringify([COSE_WITH_SHA256, PKCS7_WITH_SHA1]), + "Got expected signedTypes on activeTheme" + ); + + AddonTestUtils.useRealCertChecks = false; + await addon.startupPromise; + await addon.uninstall(); + await theme.startupPromise; + await theme.uninstall(); +}); + add_task(async function test_signedAddon() { AddonTestUtils.useRealCertChecks = true; diff --git a/toolkit/components/telemetry/tests/unit/xpcshell.toml b/toolkit/components/telemetry/tests/unit/xpcshell.toml index 0c660888ed..ea5f6f418c 100644 --- a/toolkit/components/telemetry/tests/unit/xpcshell.toml +++ b/toolkit/components/telemetry/tests/unit/xpcshell.toml @@ -18,6 +18,7 @@ support-files = [ "testNoPDBAArch64.dll", "!/toolkit/mozapps/extensions/test/xpcshell/head_addons.js", "../../../../mozapps/extensions/test/xpinstall/amosigned.xpi", + "../../../../mozapps/extensions/test/xpcshell/data/webext-implicit-id.xpi", ] generated-files = [ "system.xpi", |