diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:37 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-06-12 05:35:37 +0000 |
commit | a90a5cba08fdf6c0ceb95101c275108a152a3aed (patch) | |
tree | 532507288f3defd7f4dcf1af49698bcb76034855 /toolkit/components/telemetry/core/TelemetryHistogram.cpp | |
parent | Adding debian version 126.0.1-1. (diff) | |
download | firefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.tar.xz firefox-a90a5cba08fdf6c0ceb95101c275108a152a3aed.zip |
Merging upstream version 127.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/telemetry/core/TelemetryHistogram.cpp')
-rw-r--r-- | toolkit/components/telemetry/core/TelemetryHistogram.cpp | 647 |
1 files changed, 0 insertions, 647 deletions
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; -} |