--- a/dom/base/test/browser_use_counters.js +++ b/dom/base/test/browser_use_counters.js @@ -1,14 +1,13 @@ /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */ requestLongerTimeout(2); var {Promise: promise} = Cu.import("resource://gre/modules/Promise.jsm", {}); -Cu.import("resource://gre/modules/Services.jsm"); const gHttpTestRoot = "http://example.com/browser/dom/base/test/"; /** * Enable local telemetry recording for the duration of the tests. */ var gOldContentCanRecord = false; add_task(function* test_initialize() { @@ -100,42 +99,46 @@ function waitForPageLoad(browser) { removeEventListener("load", listener, true); resolve(); } addEventListener("load", listener, true); }); }); } -function grabHistogramsFromContent(use_counter_middlefix, page_before = null) { - let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); - let suffix = Services.appinfo.browserTabsRemoteAutostart ? "#content" : ""; - let gather = () => [ - telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_PAGE" + suffix).snapshot().sum, - telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_DOCUMENT" + suffix).snapshot().sum, - telemetry.getHistogramById("CONTENT_DOCUMENTS_DESTROYED" + suffix).snapshot().sum, - telemetry.getHistogramById("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED" + suffix).snapshot().sum, - ]; - return BrowserTestUtils.waitForCondition(() => { - return page_before != telemetry.getHistogramById("USE_COUNTER2_" + use_counter_middlefix + "_PAGE" + suffix).snapshot().sum; - }).then(gather, gather); +function grabHistogramsFromContent(browser, use_counter_middlefix) { + return ContentTask.spawn(browser, { middlefix: use_counter_middlefix }, function* (arg) { + let telemetry = Cc["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry); + function snapshot_histogram(name) { + return telemetry.getHistogramById(name).snapshot(); + } + + let histogram_page_name = "USE_COUNTER2_" + arg.middlefix + "_PAGE"; + let histogram_document_name = "USE_COUNTER2_" + arg.middlefix + "_DOCUMENT"; + let histogram_page = snapshot_histogram(histogram_page_name); + let histogram_document = snapshot_histogram(histogram_document_name); + let histogram_docs = snapshot_histogram("CONTENT_DOCUMENTS_DESTROYED"); + let histogram_toplevel_docs = snapshot_histogram("TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED"); + return [histogram_page.sum, histogram_document.sum, + histogram_docs.sum, histogram_toplevel_docs.sum]; + }); } var check_use_counter_iframe = Task.async(function* (file, use_counter_middlefix, check_documents=true) { info("checking " + file + " with histogram " + use_counter_middlefix); let newTab = gBrowser.addTab( "about:blank"); gBrowser.selectedTab = newTab; newTab.linkedBrowser.stop(); // Hold on to the current values of the telemetry histograms we're // interested in. let [histogram_page_before, histogram_document_before, histogram_docs_before, histogram_toplevel_docs_before] = - yield grabHistogramsFromContent(use_counter_middlefix); + yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix); gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html"); yield waitForPageLoad(gBrowser.selectedBrowser); // Inject our desired file into the iframe of the newly-loaded page. yield ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) { Cu.import("resource://gre/modules/PromiseUtils.jsm"); let deferred = PromiseUtils.defer(); @@ -143,17 +146,17 @@ var check_use_counter_iframe = Task.asyn let wu = content.window.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils); let iframe = content.document.getElementById('content'); iframe.src = opts.file; let listener = (event) => { event.target.removeEventListener("load", listener, true); // We flush the main document first, then the iframe's document to - // ensure any propagation that might happen from content->parent should + // ensure any propagation that might happen from child->parent should // have already happened when counters are reported to telemetry. wu.forceUseCounterFlush(content.document); wu.forceUseCounterFlush(iframe.contentDocument); deferred.resolve(); }; iframe.addEventListener("load", listener, true); @@ -166,17 +169,17 @@ var check_use_counter_iframe = Task.asyn // The histograms only get recorded when the document actually gets // destroyed, which might not have happened yet due to GC/CC effects, etc. // Try to force document destruction. yield waitForDestroyedDocuments(); // Grab histograms again and compare. let [histogram_page_after, histogram_document_after, histogram_docs_after, histogram_toplevel_docs_after] = - yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before); + yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix); is(histogram_page_after, histogram_page_before + 1, "page counts for " + use_counter_middlefix + " after are correct"); ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1, "top level document counts are correct"); if (check_documents) { is(histogram_document_after, histogram_document_before + 1, "document counts for " + use_counter_middlefix + " after are correct"); @@ -189,17 +192,17 @@ var check_use_counter_img = Task.async(f let newTab = gBrowser.addTab("about:blank"); gBrowser.selectedTab = newTab; newTab.linkedBrowser.stop(); // Hold on to the current values of the telemetry histograms we're // interested in. let [histogram_page_before, histogram_document_before, histogram_docs_before, histogram_toplevel_docs_before] = - yield grabHistogramsFromContent(use_counter_middlefix); + yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix); gBrowser.selectedBrowser.loadURI(gHttpTestRoot + "file_use_counter_outer.html"); yield waitForPageLoad(gBrowser.selectedBrowser); // Inject our desired file into the img of the newly-loaded page. yield ContentTask.spawn(gBrowser.selectedBrowser, { file: file }, function(opts) { Cu.import("resource://gre/modules/PromiseUtils.jsm"); let deferred = PromiseUtils.defer(); @@ -231,17 +234,17 @@ var check_use_counter_img = Task.async(f // The histograms only get recorded when the document actually gets // destroyed, which might not have happened yet due to GC/CC effects, etc. // Try to force document destruction. yield waitForDestroyedDocuments(); // Grab histograms again and compare. let [histogram_page_after, histogram_document_after, histogram_docs_after, histogram_toplevel_docs_after] = - yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before); + yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix); is(histogram_page_after, histogram_page_before + 1, "page counts for " + use_counter_middlefix + " after are correct"); is(histogram_document_after, histogram_document_before + 1, "document counts for " + use_counter_middlefix + " after are correct"); ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1, "top level document counts are correct"); // 2 documents: one for the outer html page containing the <img> element, and // one for the SVG image itself. @@ -255,17 +258,17 @@ var check_use_counter_direct = Task.asyn let newTab = gBrowser.addTab( "about:blank"); gBrowser.selectedTab = newTab; newTab.linkedBrowser.stop(); // Hold on to the current values of the telemetry histograms we're // interested in. let [histogram_page_before, histogram_document_before, histogram_docs_before, histogram_toplevel_docs_before] = - yield grabHistogramsFromContent(use_counter_middlefix); + yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix); gBrowser.selectedBrowser.loadURI(gHttpTestRoot + file); yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() { Cu.import("resource://gre/modules/PromiseUtils.jsm"); yield new Promise(resolve => { let listener = () => { removeEventListener("load", listener, true); @@ -284,17 +287,17 @@ var check_use_counter_direct = Task.asyn // The histograms only get recorded when the document actually gets // destroyed, which might not have happened yet due to GC/CC effects, etc. // Try to force document destruction. yield waitForDestroyedDocuments(); // Grab histograms again and compare. let [histogram_page_after, histogram_document_after, histogram_docs_after, histogram_toplevel_docs_after] = - yield grabHistogramsFromContent(use_counter_middlefix, histogram_page_before); + yield grabHistogramsFromContent(gBrowser.selectedBrowser, use_counter_middlefix); (xfail ? todo_is : is)(histogram_page_after, histogram_page_before + 1, "page counts for " + use_counter_middlefix + " after are correct"); (xfail ? todo_is : is)(histogram_document_after, histogram_document_before + 1, "document counts for " + use_counter_middlefix + " after are correct"); ok(histogram_toplevel_docs_after >= histogram_toplevel_docs_before + 1, "top level document counts are correct"); ok(histogram_docs_after >= histogram_docs_before + 1, "document counts are correct");
--- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -5399,24 +5399,8 @@ ContentParent::SendGetFilesResponseAndFo const GetFilesResponseResult& aResult) { GetFilesHelper* helper = mGetFilesPendingRequests.GetWeak(aUUID); if (helper) { mGetFilesPendingRequests.Remove(aUUID); Unused << SendGetFilesResponse(aUUID, aResult); } } - -bool -ContentParent::RecvAccumulateChildHistogram( - InfallibleTArray<Accumulation>&& aAccumulations) -{ - Telemetry::AccumulateChild(aAccumulations); - return true; -} - -bool -ContentParent::RecvAccumulateChildKeyedHistogram( - InfallibleTArray<KeyedAccumulation>&& aAccumulations) -{ - Telemetry::AccumulateChildKeyed(aAccumulations); - return true; -}
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -1129,20 +1129,16 @@ private: virtual bool RecvNotifyLowMemory() override; virtual bool RecvGetFilesRequest(const nsID& aID, const nsString& aDirectoryPath, const bool& aRecursiveFlag) override; virtual bool RecvDeleteGetFilesRequest(const nsID& aID) override; - virtual bool RecvAccumulateChildHistogram( - InfallibleTArray<Accumulation>&& aAccumulations) override; - virtual bool RecvAccumulateChildKeyedHistogram( - InfallibleTArray<KeyedAccumulation>&& aAccumulations) override; public: void SendGetFilesResponseAndForget(const nsID& aID, const GetFilesResponseResult& aResult); private: // If you add strong pointers to cycle collected objects here, be sure to // release these objects in ShutDownProcess. See the comment there for more
--- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -95,18 +95,16 @@ using mozilla::dom::ContentParentId from using mozilla::LayoutDeviceIntPoint from "Units.h"; using struct LookAndFeelInt from "mozilla/widget/WidgetMessageUtils.h"; using class mozilla::dom::MessagePort from "mozilla/dom/MessagePort.h"; using class mozilla::dom::ipc::StructuredCloneData from "mozilla/dom/ipc/StructuredCloneData.h"; using mozilla::DataStorageType from "ipc/DataStorageIPCUtils.h"; using mozilla::DocShellOriginAttributes from "mozilla/ipc/BackgroundUtils.h"; using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h"; using struct mozilla::dom::FlyWebPublishOptions from "mozilla/dom/FlyWebPublishOptionsIPCSerializer.h"; -using mozilla::Telemetry::Accumulation from "mozilla/TelemetryComms.h"; -using mozilla::Telemetry::KeyedAccumulation from "mozilla/TelemetryComms.h"; union ChromeRegistryItem { ChromePackage; OverrideMapping; SubstitutionMapping; }; @@ -1213,22 +1211,16 @@ parent: async GetFilesRequest(nsID aID, nsString aDirectory, bool aRecursiveFlag); async DeleteGetFilesRequest(nsID aID); async StoreAndBroadcastBlobURLRegistration(nsCString url, PBlob blob, Principal principal); async UnstoreAndBroadcastBlobURLUnregistration(nsCString url); - /** - * Messages for communicating child Telemetry to the parent process - */ - async AccumulateChildHistogram(Accumulation[] accumulations); - async AccumulateChildKeyedHistogram(KeyedAccumulation[] accumulations); - both: async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, Principal aPrincipal, ClonedMessageData aData); /** * Notify `push-subscription-modified` observers in the parent and child. */ async NotifyPushSubscriptionModifiedObservers(nsCString scope,
--- a/toolkit/components/telemetry/Telemetry.cpp +++ b/toolkit/components/telemetry/Telemetry.cpp @@ -2354,23 +2354,16 @@ TelemetryImpl::SnapshotScalars(unsigned NS_IMETHODIMP TelemetryImpl::ClearScalars() { TelemetryScalar::ClearScalars(); return NS_OK; } -NS_IMETHODIMP -TelemetryImpl::FlushBatchedChildTelemetry() -{ - TelemetryHistogram::IPCTimerFired(nullptr, nullptr); - return NS_OK; -} - size_t TelemetryImpl::SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) { size_t n = aMallocSizeOf(this); // Ignore the hashtables in mAddonMap; they are not significant. n += TelemetryHistogram::GetMapShallowSizesOfExcludingThis(aMallocSizeOf); n += TelemetryScalar::GetMapShallowSizesOfExcludingThis(aMallocSizeOf); @@ -2807,28 +2800,16 @@ AccumulateCategorical(ID id, const nsCSt void AccumulateTimeDelta(ID aHistogram, TimeStamp start, TimeStamp end) { Accumulate(aHistogram, static_cast<uint32_t>((end - start).ToMilliseconds())); } void -AccumulateChild(const nsTArray<Accumulation>& aAccumulations) -{ - TelemetryHistogram::AccumulateChild(aAccumulations); -} - -void -AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations) -{ - TelemetryHistogram::AccumulateChildKeyed(aAccumulations); -} - -void ClearHistogram(ID aId) { TelemetryHistogram::ClearHistogram(aId); } const char* GetHistogramName(ID id) {
--- a/toolkit/components/telemetry/Telemetry.h +++ b/toolkit/components/telemetry/Telemetry.h @@ -28,19 +28,16 @@ *****************************************************************************/ namespace mozilla { namespace HangMonitor { class HangAnnotations; } // namespace HangMonitor namespace Telemetry { -struct Accumulation; -struct KeyedAccumulation; - enum TimerResolution { Millisecond, Microsecond }; /** * Create and destroy the underlying base::StatisticsRecorder singleton. * Creation has to be done very early in the startup sequence. @@ -124,30 +121,16 @@ void AccumulateCategorical(ID id, const * * @param id - histogram id * @param start - start time * @param end - end time */ void AccumulateTimeDelta(ID id, TimeStamp start, TimeStamp end = TimeStamp::Now()); /** - * Accumulate child data into child histograms - * - * @param aAccumulations - accumulation actions to perform - */ -void AccumulateChild(const nsTArray<Accumulation>& aAccumulations); - -/** - * Accumulate child data into child keyed histograms - * - * @param aAccumulations - accumulation actions to perform - */ -void AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations); - -/** * This clears the data for a histogram in TelemetryHistogramEnums.h. * * @param id - histogram id */ void ClearHistogram(ID id); /** * Enable/disable recording for this histogram at runtime.
deleted file mode 100644 --- a/toolkit/components/telemetry/TelemetryComms.h +++ /dev/null @@ -1,84 +0,0 @@ -/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ -/* 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 - */ - -#ifndef Telemetry_Comms_h__ -#define Telemetry_Comms_h__ - -#include "ipc/IPCMessageUtils.h" - -namespace mozilla { -namespace Telemetry { - -enum ID : uint32_t; - -struct Accumulation -{ - mozilla::Telemetry::ID mId; - uint32_t mSample; -}; - -struct KeyedAccumulation -{ - mozilla::Telemetry::ID mId; - uint32_t mSample; - nsCString mKey; -}; - -} // namespace Telemetry -} // namespace mozilla - -namespace IPC { - -template<> -struct -ParamTraits<mozilla::Telemetry::Accumulation> -{ - typedef mozilla::Telemetry::Accumulation paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - aMsg->WriteUInt32(aParam.mId); - WriteParam(aMsg, aParam.mSample); - } - - static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) - { - if (!aMsg->ReadUInt32(aIter, reinterpret_cast<uint32_t*>(&(aResult->mId))) || - !ReadParam(aMsg, aIter, &(aResult->mSample))) { - return false; - } - - return true; - } -}; - -template<> -struct -ParamTraits<mozilla::Telemetry::KeyedAccumulation> -{ - typedef mozilla::Telemetry::KeyedAccumulation paramType; - - static void Write(Message* aMsg, const paramType& aParam) - { - aMsg->WriteUInt32(aParam.mId); - WriteParam(aMsg, aParam.mSample); - WriteParam(aMsg, aParam.mKey); - } - - static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) - { - if (!aMsg->ReadUInt32(aIter, reinterpret_cast<uint32_t*>(&(aResult->mId))) || - !ReadParam(aMsg, aIter, &(aResult->mSample)) || - !ReadParam(aMsg, aIter, &(aResult->mKey))) { - return false; - } - - return true; - } -}; - -} // namespace IPC - -#endif // Telemetry_Comms_h__
--- a/toolkit/components/telemetry/TelemetryHistogram.cpp +++ b/toolkit/components/telemetry/TelemetryHistogram.cpp @@ -9,40 +9,33 @@ #include "js/GCAPI.h" #include "nsString.h" #include "nsTHashtable.h" #include "nsHashKeys.h" #include "nsBaseHashtable.h" #include "nsClassHashtable.h" #include "nsITelemetry.h" -#include "mozilla/dom/ContentChild.h" #include "mozilla/dom/ToJSValue.h" -#include "mozilla/Atomics.h" #include "mozilla/StartupTimeline.h" #include "mozilla/StaticMutex.h" -#include "mozilla/StaticPtr.h" -#include "mozilla/Unused.h" #include "TelemetryCommon.h" #include "TelemetryHistogram.h" #include "base/histogram.h" using base::Histogram; using base::StatisticsRecorder; using base::BooleanHistogram; using base::CountHistogram; using base::FlagHistogram; using base::LinearHistogram; using mozilla::StaticMutex; using mozilla::StaticMutexAutoLock; -using mozilla::StaticAutoPtr; -using mozilla::Telemetry::Accumulation; -using mozilla::Telemetry::KeyedAccumulation; //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // Naming: there are two kinds of functions in this file: // // * Functions named internal_*: these can only be reached via an @@ -94,17 +87,16 @@ using mozilla::Telemetry::KeyedAccumulat //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // PRIVATE TYPES #define EXPIRED_ID "__expired__" #define SUBSESSION_HISTOGRAM_PREFIX "sub#" #define KEYED_HISTOGRAM_NAME_SEPARATOR "#" -#define CHILD_HISTOGRAM_SUFFIX "#content" namespace { using mozilla::Telemetry::Common::AutoHashtable; using mozilla::Telemetry::Common::IsExpiredVersion; using mozilla::Telemetry::Common::CanRecordDataset; using mozilla::Telemetry::Common::IsInDataset; @@ -188,23 +180,16 @@ bool gCorruptHistograms[mozilla::Telemet // This is for gHistograms, gHistogramStringTable #include "TelemetryHistogramData.inc" AddonMapType gAddonMap; // The singleton StatisticsRecorder object for this process. base::StatisticsRecorder* gStatisticsRecorder = nullptr; -// For batching and sending child process accumulations to the parent -nsITimer* gIPCTimer = nullptr; -mozilla::Atomic<bool, mozilla::Relaxed> gIPCTimerArmed(false); -mozilla::Atomic<bool, mozilla::Relaxed> gIPCTimerArming(false); -StaticAutoPtr<nsTArray<Accumulation>> gAccumulations; -StaticAutoPtr<nsTArray<KeyedAccumulation>> gKeyedAccumulations; - } // namespace //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // PRIVATE CONSTANTS @@ -214,22 +199,16 @@ namespace { const mozilla::Telemetry::ID kRecordingInitiallyDisabledIDs[] = { mozilla::Telemetry::FX_REFRESH_DRIVER_SYNC_SCROLL_FRAME_DELAY_MS, // The array must not be empty. Leave these item here. mozilla::Telemetry::TELEMETRY_TEST_COUNT_INIT_NO_RECORD, mozilla::Telemetry::TELEMETRY_TEST_KEYED_COUNT_INIT_NO_RECORD }; -// Sending each remote accumulation immediately places undue strain on the -// IPC subsystem. Batch the remote accumulations for a period of time before -// sending them all at once. This value was chosen as a balance between data -// timeliness and performance (see bug 1218576) -const uint32_t kBatchTimeoutMs = 2000; - } // namespace //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // PRIVATE: Misc small helpers @@ -333,26 +312,16 @@ HistogramInfo::label_id(const char* labe *labelId = i; return NS_OK; } } return NS_ERROR_FAILURE; } -bool -StringEndsWith(const std::string& name, const std::string& suffix) -{ - if (name.size() < suffix.size()) { - return false; - } - - return name.compare(name.size() - suffix.size(), suffix.size(), suffix) == 0; -} - } // namespace //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // PRIVATE: Histogram Get, Add, Clone, Clear functions @@ -427,70 +396,49 @@ internal_HistogramGet(const char *name, break; default: NS_ASSERTION(false, "Invalid histogram type"); return NS_ERROR_INVALID_ARG; } return NS_OK; } -CharPtrEntryType* -internal_GetHistogramMapEntry(const char* name) -{ - nsDependentCString histogramName(name); - NS_NAMED_LITERAL_CSTRING(suffix, CHILD_HISTOGRAM_SUFFIX); - if (!StringEndsWith(histogramName, suffix)) { - return gHistogramMap.GetEntry(name); - } - auto root = Substring(histogramName, 0, histogramName.Length() - suffix.Length()); - return gHistogramMap.GetEntry(PromiseFlatCString(root).get()); -} - nsresult internal_GetHistogramEnumId(const char *name, mozilla::Telemetry::ID *id) { if (!gInitDone) { return NS_ERROR_FAILURE; } - CharPtrEntryType *entry = internal_GetHistogramMapEntry(name); + CharPtrEntryType *entry = gHistogramMap.GetEntry(name); if (!entry) { return NS_ERROR_INVALID_ARG; } *id = entry->mData; return NS_OK; } // O(1) histogram lookup by numeric id nsresult -internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret, - bool child = false) +internal_GetHistogramByEnumId(mozilla::Telemetry::ID id, Histogram **ret) { static Histogram* knownHistograms[mozilla::Telemetry::HistogramCount] = {0}; - static Histogram* knownChildHistograms[mozilla::Telemetry::HistogramCount] = {0}; - Histogram *h = child ? knownChildHistograms[id] : knownHistograms[id]; + Histogram *h = knownHistograms[id]; if (h) { *ret = h; return NS_OK; } const HistogramInfo &p = gHistograms[id]; if (p.keyed) { return NS_ERROR_FAILURE; } - nsCString histogramName; - histogramName.Append(p.id()); - if (child) { - histogramName.AppendLiteral(CHILD_HISTOGRAM_SUFFIX); - } - - nsresult rv = internal_HistogramGet(histogramName.get(), p.expiration(), - p.histogramType, p.min, p.max, - p.bucketCount, true, &h); + nsresult rv = internal_HistogramGet(p.id(), p.expiration(), p.histogramType, + p.min, p.max, p.bucketCount, true, &h); if (NS_FAILED(rv)) return rv; #ifdef DEBUG // Check that the C++ Histogram code computes the same ranges as the // Python histogram code. if (!IsExpiredVersion(p.expiration())) { const struct bounds &b = gBucketLowerBoundIndex[id]; @@ -500,37 +448,31 @@ internal_GetHistogramByEnumId(mozilla::T for (int i = 0; i < b.length; ++i) { MOZ_ASSERT(gBucketLowerBounds[b.offset + i] == h->ranges(i), "C++/Python bucket mismatch"); } } } #endif - if (child) { - *ret = knownChildHistograms[id] = h; - } else { - *ret = knownHistograms[id] = h; - } + *ret = knownHistograms[id] = h; return NS_OK; } nsresult internal_GetHistogramByName(const nsACString &name, Histogram **ret) { mozilla::Telemetry::ID id; nsresult rv = internal_GetHistogramEnumId(PromiseFlatCString(name).get(), &id); if (NS_FAILED(rv)) { return rv; } - bool isChild = StringEndsWith(name, - NS_LITERAL_CSTRING(CHILD_HISTOGRAM_SUFFIX)); - rv = internal_GetHistogramByEnumId(id, ret, isChild); + rv = internal_GetHistogramByEnumId(id, ret); if (NS_FAILED(rv)) return rv; return NS_OK; } /** * This clones a histogram |existing| with the id |existingId| to a @@ -576,53 +518,42 @@ internal_CloneHistogram(const nsACString if (NS_FAILED(rv)) { return nullptr; } return internal_CloneHistogram(newName, existingId, *existing); } #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) - Histogram* internal_GetSubsessionHistogram(Histogram& existing) { mozilla::Telemetry::ID id; nsresult rv = internal_GetHistogramEnumId(existing.histogram_name().c_str(), &id); if (NS_FAILED(rv) || gHistograms[id].keyed) { return nullptr; } - bool isChild = StringEndsWith(existing.histogram_name(), - CHILD_HISTOGRAM_SUFFIX); - static Histogram* subsession[mozilla::Telemetry::HistogramCount] = {}; - static Histogram* subsessionChild[mozilla::Telemetry::HistogramCount] = {}; - Histogram* cached = isChild ? subsessionChild[id] : subsession[id]; - if (cached) { - return cached; + if (subsession[id]) { + return subsession[id]; } NS_NAMED_LITERAL_CSTRING(prefix, SUBSESSION_HISTOGRAM_PREFIX); nsDependentCString existingName(gHistograms[id].id()); if (StringBeginsWith(existingName, prefix)) { return nullptr; } nsCString subsessionName(prefix); - subsessionName.Append(existing.histogram_name().c_str()); + subsessionName.Append(existingName); - Histogram* clone = internal_CloneHistogram(subsessionName, id, existing); - if (isChild) { - subsessionChild[id] = clone; - } else { - subsession[id] = clone; - } - return clone; + subsession[id] = internal_CloneHistogram(subsessionName, id, existing); + return subsession[id]; } #endif nsresult internal_HistogramAdd(Histogram& histogram, int32_t value, uint32_t dataset) { // Check if we are allowed to record the data. bool canRecordDataset = CanRecordDataset(dataset, @@ -661,23 +592,36 @@ internal_HistogramAdd(Histogram& histogr return NS_OK; } dataset = gHistograms[id].dataset; } return internal_HistogramAdd(histogram, value, dataset); } +nsresult +internal_HistogramAddCategorical(mozilla::Telemetry::ID id, const nsCString& label) +{ + uint32_t labelId = 0; + if (NS_FAILED(gHistograms[id].label_id(label.get(), &labelId))) { + return NS_ERROR_ILLEGAL_VALUE; + } + + Histogram* h = nullptr; + nsresult rv = internal_GetHistogramByEnumId(id, &h); + if (NS_FAILED(rv)) { + return rv; + } + + return internal_HistogramAdd(*h, labelId); +} + void internal_HistogramClear(Histogram& aHistogram, bool onlySubsession) { - MOZ_ASSERT(XRE_IsParentProcess()); - if (!XRE_IsParentProcess()) { - return; - } if (!onlySubsession) { aHistogram.Clear(); } #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) if (Histogram* subsession = internal_GetSubsessionHistogram(aHistogram)) { subsession->Clear(); } @@ -863,18 +807,16 @@ public: bool subsession, bool clearSubsession); void SetRecordingEnabled(bool aEnabled) { mRecordingEnabled = aEnabled; }; bool IsRecordingEnabled() const { return mRecordingEnabled; }; nsresult Add(const nsCString& key, uint32_t aSample); void Clear(bool subsession); - nsresult GetEnumId(mozilla::Telemetry::ID& id); - private: typedef nsBaseHashtableET<nsCStringHashKey, Histogram*> KeyedHistogramEntry; typedef AutoHashtable<KeyedHistogramEntry> KeyedHistogramMapType; KeyedHistogramMapType mHistogramMap; #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) KeyedHistogramMapType mSubsessionMap; #endif @@ -1007,20 +949,16 @@ KeyedHistogram::Add(const nsCString& key subsession->Add(sample); #endif return NS_OK; } void KeyedHistogram::Clear(bool onlySubsession) { - MOZ_ASSERT(XRE_IsParentProcess()); - if (!XRE_IsParentProcess()) { - return; - } #if !defined(MOZ_WIDGET_GONK) && !defined(MOZ_WIDGET_ANDROID) for (auto iter = mSubsessionMap.Iter(); !iter.Done(); iter.Next()) { iter.Get()->mData->Clear(); } mSubsessionMap.Clear(); if (onlySubsession) { return; } @@ -1098,22 +1036,16 @@ KeyedHistogram::GetJSSnapshot(JSContext* if (subsession && clearSubsession) { Clear(true); } #endif return NS_OK; } -nsresult -KeyedHistogram::GetEnumId(mozilla::Telemetry::ID& id) -{ - return internal_GetHistogramEnumId(mName.get(), &id); -} - } // namespace //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // // PRIVATE: KeyedHistogram helpers @@ -1132,315 +1064,16 @@ internal_GetKeyedHistogramById(const nsA } } // namespace //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // -// PRIVATE: functions related to addon histograms - -namespace { - -// Compute the name to pass into Histogram for the addon histogram -// 'name' from the addon 'id'. We can't use 'name' directly because it -// might conflict with other histograms in other addons or even with our -// own. -void -internal_AddonHistogramName(const nsACString &id, const nsACString &name, - nsACString &ret) -{ - ret.Append(id); - ret.Append(':'); - ret.Append(name); -} - -bool -internal_CreateHistogramForAddon(const nsACString &name, - AddonHistogramInfo &info) -{ - Histogram *h; - nsresult rv = internal_HistogramGet(PromiseFlatCString(name).get(), "never", - info.histogramType, info.min, info.max, - info.bucketCount, true, &h); - if (NS_FAILED(rv)) { - return false; - } - // Don't let this histogram be reported via the normal means - // (e.g. Telemetry.registeredHistograms); we'll make it available in - // other ways. - h->ClearFlags(Histogram::kUmaTargetedHistogramFlag); - info.h = h; - return true; -} - -bool -internal_AddonHistogramReflector(AddonHistogramEntryType *entry, - JSContext *cx, JS::Handle<JSObject*> obj) -{ - AddonHistogramInfo &info = entry->mData; - - // Never even accessed the histogram. - if (!info.h) { - // Have to force creation of HISTOGRAM_FLAG histograms. - if (info.histogramType != nsITelemetry::HISTOGRAM_FLAG) - return true; - - if (!internal_CreateHistogramForAddon(entry->GetKey(), info)) { - return false; - } - } - - if (internal_IsEmpty(info.h)) { - return true; - } - - JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx)); - if (!snapshot) { - // Just consider this to be skippable. - return true; - } - switch (internal_ReflectHistogramSnapshot(cx, snapshot, info.h)) { - case REFLECT_FAILURE: - case REFLECT_CORRUPT: - return false; - case REFLECT_OK: - const nsACString &histogramName = entry->GetKey(); - if (!JS_DefineProperty(cx, obj, PromiseFlatCString(histogramName).get(), - snapshot, JSPROP_ENUMERATE)) { - return false; - } - break; - } - return true; -} - -bool -internal_AddonReflector(AddonEntryType *entry, JSContext *cx, - JS::Handle<JSObject*> obj) -{ - const nsACString &addonId = entry->GetKey(); - JS::Rooted<JSObject*> subobj(cx, JS_NewPlainObject(cx)); - if (!subobj) { - return false; - } - - AddonHistogramMapType *map = entry->mData; - if (!(map->ReflectIntoJS(internal_AddonHistogramReflector, cx, subobj) - && JS_DefineProperty(cx, obj, PromiseFlatCString(addonId).get(), - subobj, JSPROP_ENUMERATE))) { - return false; - } - return true; -} - -} // namespace - - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -// -// PRIVATE: thread-unsafe helpers for the external interface - -// This is a StaticMutex rather than a plain Mutex (1) so that -// it gets initialised in a thread-safe manner the first time -// it is used, and (2) because it is never de-initialised, and -// a normal Mutex would show up as a leak in BloatView. StaticMutex -// also has the "OffTheBooks" property, so it won't show as a leak -// in BloatView. -static StaticMutex gTelemetryHistogramMutex; - -namespace { - -void -internal_SetHistogramRecordingEnabled(mozilla::Telemetry::ID aID, bool aEnabled) -{ - if (!internal_IsHistogramEnumId(aID)) { - MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) must be used with an enum id"); - return; - } - - if (gHistograms[aID].keyed) { - const nsDependentCString id(gHistograms[aID].id()); - KeyedHistogram* keyed = internal_GetKeyedHistogramById(id); - if (keyed) { - keyed->SetRecordingEnabled(aEnabled); - return; - } - } else { - Histogram *h; - nsresult rv = internal_GetHistogramByEnumId(aID, &h); - if (NS_SUCCEEDED(rv)) { - h->SetRecordingEnabled(aEnabled); - return; - } - } - - MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found"); -} - -void internal_armIPCTimerMainThread() -{ - MOZ_ASSERT(NS_IsMainThread()); - gIPCTimerArming = false; - if (gIPCTimerArmed) { - return; - } - if (!gIPCTimer) { - CallCreateInstance(NS_TIMER_CONTRACTID, &gIPCTimer); - } - if (gIPCTimer) { - gIPCTimer->InitWithFuncCallback(TelemetryHistogram::IPCTimerFired, - nullptr, kBatchTimeoutMs, - nsITimer::TYPE_ONE_SHOT); - gIPCTimerArmed = true; - } -} - -void internal_armIPCTimer() -{ - if (gIPCTimerArmed || gIPCTimerArming) { - return; - } - gIPCTimerArming = true; - if (NS_IsMainThread()) { - internal_armIPCTimerMainThread(); - } else { - NS_DispatchToMainThread(NS_NewRunnableFunction([]() -> void { - StaticMutexAutoLock locker(gTelemetryHistogramMutex); - internal_armIPCTimerMainThread(); - })); - } -} - -bool -internal_RemoteAccumulate(mozilla::Telemetry::ID aId, uint32_t aSample) -{ - if (XRE_IsParentProcess()) { - return false; - } - if (!gAccumulations) { - gAccumulations = new nsTArray<Accumulation>(); - } - gAccumulations->AppendElement(Accumulation{aId, aSample}); - internal_armIPCTimer(); - return true; -} - -bool -internal_RemoteAccumulate(mozilla::Telemetry::ID aId, - const nsCString& aKey, uint32_t aSample) -{ - if (XRE_IsParentProcess()) { - return false; - } - if (!gKeyedAccumulations) { - gKeyedAccumulations = new nsTArray<KeyedAccumulation>(); - } - gKeyedAccumulations->AppendElement(KeyedAccumulation{aId, aSample, aKey}); - internal_armIPCTimer(); - return true; -} - -void internal_Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample) -{ - if (!internal_CanRecordBase() || - internal_RemoteAccumulate(aHistogram, aSample)) { - return; - } - Histogram *h; - nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h); - if (NS_SUCCEEDED(rv)) { - internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset); - } -} - -void -internal_Accumulate(mozilla::Telemetry::ID aID, - const nsCString& aKey, uint32_t aSample) -{ - if (!gInitDone || !internal_CanRecordBase() || - internal_RemoteAccumulate(aID, aKey, aSample)) { - return; - } - const HistogramInfo& th = gHistograms[aID]; - KeyedHistogram* keyed - = internal_GetKeyedHistogramById(nsDependentCString(th.id())); - MOZ_ASSERT(keyed); - keyed->Add(aKey, aSample); -} - -void -internal_Accumulate(Histogram& aHistogram, uint32_t aSample) -{ - if (XRE_IsParentProcess()) { - internal_HistogramAdd(aHistogram, aSample); - return; - } - - mozilla::Telemetry::ID id; - nsresult rv = internal_GetHistogramEnumId(aHistogram.histogram_name().c_str(), &id); - if (NS_SUCCEEDED(rv)) { - internal_RemoteAccumulate(id, aSample); - } -} - -void -internal_Accumulate(KeyedHistogram& aKeyed, - const nsCString& aKey, uint32_t aSample) -{ - if (XRE_IsParentProcess()) { - aKeyed.Add(aKey, aSample); - return; - } - - mozilla::Telemetry::ID id; - if (NS_SUCCEEDED(aKeyed.GetEnumId(id))) { - internal_RemoteAccumulate(id, aKey, aSample); - } -} - -void -internal_AccumulateChild(mozilla::Telemetry::ID aId, uint32_t aSample) -{ - if (!internal_CanRecordBase()) { - return; - } - Histogram* h; - nsresult rv = internal_GetHistogramByEnumId(aId, &h, true); - if (NS_SUCCEEDED(rv)) { - internal_HistogramAdd(*h, aSample, gHistograms[aId].dataset); - } else { - NS_WARNING("NS_FAILED GetHistogramByEnumId for CHILD"); - } -} - -void -internal_AccumulateChildKeyed(mozilla::Telemetry::ID aId, - const nsCString& aKey, uint32_t aSample) -{ - if (!gInitDone || !internal_CanRecordBase()) { - return; - } - const HistogramInfo& th = gHistograms[aId]; - nsCString id; - id.Append(th.id()); - id.AppendLiteral(CHILD_HISTOGRAM_SUFFIX); - KeyedHistogram* keyed = internal_GetKeyedHistogramById(id); - MOZ_ASSERT(keyed); - keyed->Add(aKey, aSample); -} - -} // namespace - - -//////////////////////////////////////////////////////////////////////// -//////////////////////////////////////////////////////////////////////// -// // PRIVATE: JSHistogram_* functions // NOTE: the functions in this section: // // internal_JSHistogram_Add // internal_JSHistogram_Snapshot // internal_JSHistogram_Clear // internal_JSHistogram_Dataset @@ -1470,57 +1103,62 @@ internal_JSHistogram_Add(JSContext *cx, Histogram::ClassType type = h->histogram_type(); JS::CallArgs args = CallArgsFromVp(argc, vp); if (!internal_CanRecordBase()) { return true; } - uint32_t value = 0; - mozilla::Telemetry::ID id; + // If we don't have an argument for the count histogram, assume an increment of 1. + // Otherwise, make sure to run some sanity checks on the argument. if ((type == base::CountHistogram::COUNT_HISTOGRAM) && (args.length() == 0)) { - // If we don't have an argument for the count histogram, assume an increment of 1. - // Otherwise, make sure to run some sanity checks on the argument. - value = 1; - } else if (type == base::LinearHistogram::LINEAR_HISTOGRAM && + internal_HistogramAdd(*h, 1); + return true; + } + + // For categorical histograms we allow passing a string argument that specifies the label. + mozilla::Telemetry::ID id; + if (type == base::LinearHistogram::LINEAR_HISTOGRAM && (args.length() > 0) && args[0].isString() && NS_SUCCEEDED(internal_GetHistogramEnumId(h->histogram_name().c_str(), &id)) && gHistograms[id].histogramType == nsITelemetry::HISTOGRAM_CATEGORICAL) { - // For categorical histograms we allow passing a string argument that specifies the label. nsAutoJSString label; if (!label.init(cx, args[0])) { JS_ReportError(cx, "Invalid string parameter"); return false; } - nsresult rv = gHistograms[id].label_id(NS_ConvertUTF16toUTF8(label).get(), &value); + nsresult rv = internal_HistogramAddCategorical(id, NS_ConvertUTF16toUTF8(label)); if (NS_FAILED(rv)) { JS_ReportError(cx, "Unknown label for categorical histogram"); return false; } - } else { - // All other accumulations expect one numerical argument. - if (!args.length()) { - JS_ReportError(cx, "Expected one argument"); - return false; - } + + return true; + } - if (!(args[0].isNumber() || args[0].isBoolean())) { - JS_ReportError(cx, "Not a number"); - return false; - } - - if (!JS::ToUint32(cx, args[0], &value)) { - JS_ReportError(cx, "Failed to convert argument"); - return false; - } + // All other accumulations expect one numerical argument. + int32_t value = 0; + if (!args.length()) { + JS_ReportError(cx, "Expected one argument"); + return false; } - internal_Accumulate(*h, value); + if (!(args[0].isNumber() || args[0].isBoolean())) { + JS_ReportError(cx, "Not a number"); + return false; + } + + if (!JS::ToInt32(cx, args[0], &value)) { + JS_ReportError(cx, "Failed to convert argument"); + return false; + } + + internal_HistogramAdd(*h, value); return true; } bool internal_JSHistogram_Snapshot(JSContext *cx, unsigned argc, JS::Value *vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); JSObject *obj = JS_THIS_OBJECT(cx, vp); @@ -1758,17 +1396,17 @@ internal_JSKeyedHistogram_Add(JSContext return false; } if (!JS::ToInt32(cx, args[1], &value)) { return false; } } - internal_Accumulate(*keyed, NS_ConvertUTF16toUTF8(key), value); + keyed->Add(NS_ConvertUTF16toUTF8(key), value); return true; } bool internal_JSKeyedHistogram_Keys(JSContext *cx, unsigned argc, JS::Value *vp) { JSObject *obj = JS_THIS_OBJECT(cx, vp); if (!obj) { @@ -1911,18 +1549,191 @@ internal_WrapAndReturnKeyedHistogram(Key } } // namespace //////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////// // +// PRIVATE: functions related to addon histograms + +namespace { + +// Compute the name to pass into Histogram for the addon histogram +// 'name' from the addon 'id'. We can't use 'name' directly because it +// might conflict with other histograms in other addons or even with our +// own. +void +internal_AddonHistogramName(const nsACString &id, const nsACString &name, + nsACString &ret) +{ + ret.Append(id); + ret.Append(':'); + ret.Append(name); +} + +bool +internal_CreateHistogramForAddon(const nsACString &name, + AddonHistogramInfo &info) +{ + Histogram *h; + nsresult rv = internal_HistogramGet(PromiseFlatCString(name).get(), "never", + info.histogramType, info.min, info.max, + info.bucketCount, true, &h); + if (NS_FAILED(rv)) { + return false; + } + // Don't let this histogram be reported via the normal means + // (e.g. Telemetry.registeredHistograms); we'll make it available in + // other ways. + h->ClearFlags(Histogram::kUmaTargetedHistogramFlag); + info.h = h; + return true; +} + +bool +internal_AddonHistogramReflector(AddonHistogramEntryType *entry, + JSContext *cx, JS::Handle<JSObject*> obj) +{ + AddonHistogramInfo &info = entry->mData; + + // Never even accessed the histogram. + if (!info.h) { + // Have to force creation of HISTOGRAM_FLAG histograms. + if (info.histogramType != nsITelemetry::HISTOGRAM_FLAG) + return true; + + if (!internal_CreateHistogramForAddon(entry->GetKey(), info)) { + return false; + } + } + + if (internal_IsEmpty(info.h)) { + return true; + } + + JS::Rooted<JSObject*> snapshot(cx, JS_NewPlainObject(cx)); + if (!snapshot) { + // Just consider this to be skippable. + return true; + } + switch (internal_ReflectHistogramSnapshot(cx, snapshot, info.h)) { + case REFLECT_FAILURE: + case REFLECT_CORRUPT: + return false; + case REFLECT_OK: + const nsACString &histogramName = entry->GetKey(); + if (!JS_DefineProperty(cx, obj, PromiseFlatCString(histogramName).get(), + snapshot, JSPROP_ENUMERATE)) { + return false; + } + break; + } + return true; +} + +bool +internal_AddonReflector(AddonEntryType *entry, JSContext *cx, + JS::Handle<JSObject*> obj) +{ + const nsACString &addonId = entry->GetKey(); + JS::Rooted<JSObject*> subobj(cx, JS_NewPlainObject(cx)); + if (!subobj) { + return false; + } + + AddonHistogramMapType *map = entry->mData; + if (!(map->ReflectIntoJS(internal_AddonHistogramReflector, cx, subobj) + && JS_DefineProperty(cx, obj, PromiseFlatCString(addonId).get(), + subobj, JSPROP_ENUMERATE))) { + return false; + } + return true; +} + +} // namespace + + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// +// PRIVATE: thread-unsafe helpers for the external interface + +namespace { + +void +internal_SetHistogramRecordingEnabled(mozilla::Telemetry::ID aID, bool aEnabled) +{ + if (!internal_IsHistogramEnumId(aID)) { + MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) must be used with an enum id"); + return; + } + + if (gHistograms[aID].keyed) { + const nsDependentCString id(gHistograms[aID].id()); + KeyedHistogram* keyed = internal_GetKeyedHistogramById(id); + if (keyed) { + keyed->SetRecordingEnabled(aEnabled); + return; + } + } else { + Histogram *h; + nsresult rv = internal_GetHistogramByEnumId(aID, &h); + if (NS_SUCCEEDED(rv)) { + h->SetRecordingEnabled(aEnabled); + return; + } + } + + MOZ_ASSERT(false, "Telemetry::SetHistogramRecordingEnabled(...) id not found"); +} + +void internal_Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample) +{ + if (!internal_CanRecordBase()) { + return; + } + Histogram *h; + nsresult rv = internal_GetHistogramByEnumId(aHistogram, &h); + if (NS_SUCCEEDED(rv)) { + internal_HistogramAdd(*h, aSample, gHistograms[aHistogram].dataset); + } +} + +void +internal_Accumulate(mozilla::Telemetry::ID aID, + const nsCString& aKey, uint32_t aSample) +{ + if (!gInitDone || !internal_CanRecordBase()) { + return; + } + const HistogramInfo& th = gHistograms[aID]; + KeyedHistogram* keyed + = internal_GetKeyedHistogramById(nsDependentCString(th.id())); + MOZ_ASSERT(keyed); + keyed->Add(aKey, aSample); +} + +} // namespace + + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// // EXTERNALLY VISIBLE FUNCTIONS in namespace TelemetryHistogram:: +// This is a StaticMutex rather than a plain Mutex (1) so that +// it gets initialised in a thread-safe manner the first time +// it is used, and (2) because it is never de-initialised, and +// a normal Mutex would show up as a leak in BloatView. StaticMutex +// also has the "OffTheBooks" property, so it won't show as a leak +// in BloatView. +static StaticMutex gTelemetryHistogramMutex; + // All of these functions are actually in namespace TelemetryHistogram::, // but the ::TelemetryHistogram prefix is given explicitly. This is // because it is critical to see which calls from these functions are // to another function in this interface. Mis-identifying "inwards // calls" from "calls to another function in this interface" will lead // to deadlocking and/or races. See comments at the top of the file // for further (important!) details. @@ -1976,26 +1787,16 @@ void TelemetryHistogram::InitializeGloba if (!h.keyed) { continue; } const nsDependentCString id(h.id()); const nsDependentCString expiration(h.expiration()); gKeyedHistograms.Put(id, new KeyedHistogram(id, expiration, h.histogramType, h.min, h.max, h.bucketCount, h.dataset)); - if (XRE_IsParentProcess()) { - // We must create registered child keyed histograms as well or else the - // same code in TelemetrySession.jsm that fails without parent keyed - // histograms will fail without child keyed histograms. - nsCString childId(id); - childId.AppendLiteral(CHILD_HISTOGRAM_SUFFIX); - gKeyedHistograms.Put(childId, - new KeyedHistogram(id, expiration, h.histogramType, - h.min, h.max, h.bucketCount, h.dataset)); - } } // Some Telemetry histograms depend on the value of C++ constants and hardcode // their values in Histograms.json. // We add static asserts here for those values to match so that future changes // don't go unnoticed. // TODO: Compare explicitly with gHistograms[<histogram id>].bucketCount here // once we can make gHistograms constexpr (requires VS2015). @@ -2016,21 +1817,16 @@ void TelemetryHistogram::InitializeGloba void TelemetryHistogram::DeInitializeGlobalState() { StaticMutexAutoLock locker(gTelemetryHistogramMutex); gCanRecordBase = false; gCanRecordExtended = false; gHistogramMap.Clear(); gKeyedHistograms.Clear(); gAddonMap.Clear(); - gAccumulations = nullptr; - gKeyedAccumulations = nullptr; - if (gIPCTimer) { - NS_RELEASE(gIPCTimer); - } gInitDone = false; } #ifdef DEBUG bool TelemetryHistogram::GlobalStateHasBeenInitialized() { StaticMutexAutoLock locker(gTelemetryHistogramMutex); return gInitDone; } @@ -2126,17 +1922,22 @@ TelemetryHistogram::Accumulate(const cha if (!internal_CanRecordBase()) { return; } mozilla::Telemetry::ID id; nsresult rv = internal_GetHistogramEnumId(name, &id); if (NS_FAILED(rv)) { return; } - internal_Accumulate(id, sample); + + Histogram *h; + rv = internal_GetHistogramByEnumId(id, &h); + if (NS_SUCCEEDED(rv)) { + internal_HistogramAdd(*h, sample, gHistograms[id].dataset); + } } void TelemetryHistogram::Accumulate(const char* name, const nsCString& key, uint32_t sample) { StaticMutexAutoLock locker(gTelemetryHistogramMutex); if (!internal_CanRecordBase()) { @@ -2149,62 +1950,17 @@ TelemetryHistogram::Accumulate(const cha } } void TelemetryHistogram::AccumulateCategorical(mozilla::Telemetry::ID aId, const nsCString& label) { StaticMutexAutoLock locker(gTelemetryHistogramMutex); - if (!internal_CanRecordBase()) { - return; - } - uint32_t labelId = 0; - if (NS_FAILED(gHistograms[aId].label_id(label.get(), &labelId))) { - return; - } - internal_Accumulate(aId, labelId); -} - -void -TelemetryHistogram::AccumulateChild(const nsTArray<Accumulation>& aAccumulations) -{ - MOZ_ASSERT(XRE_IsParentProcess()); - StaticMutexAutoLock locker(gTelemetryHistogramMutex); - if (!internal_CanRecordBase()) { - return; - } - for (uint32_t i = 0; i < aAccumulations.Length(); ++i) { - bool isValid = internal_IsHistogramEnumId(aAccumulations[i].mId); - MOZ_ASSERT(isValid); - if (!isValid) { - continue; - } - internal_AccumulateChild(aAccumulations[i].mId, aAccumulations[i].mSample); - } -} - -void -TelemetryHistogram::AccumulateChildKeyed(const nsTArray<KeyedAccumulation>& aAccumulations) -{ - MOZ_ASSERT(XRE_IsParentProcess()); - StaticMutexAutoLock locker(gTelemetryHistogramMutex); - if (!internal_CanRecordBase()) { - return; - } - for (uint32_t i = 0; i < aAccumulations.Length(); ++i) { - bool isValid = internal_IsHistogramEnumId(aAccumulations[i].mId); - MOZ_ASSERT(isValid); - if (!isValid) { - continue; - } - internal_AccumulateChildKeyed(aAccumulations[i].mId, - aAccumulations[i].mKey, - aAccumulations[i].mSample); - } + internal_HistogramAddCategorical(aId, label); } void TelemetryHistogram::ClearHistogram(mozilla::Telemetry::ID aId) { StaticMutexAutoLock locker(gTelemetryHistogramMutex); if (!internal_CanRecordBase()) { return; @@ -2339,18 +2095,16 @@ TelemetryHistogram::CreateHistogramSnaps } const uint32_t type = gHistograms[i].histogramType; if (type == nsITelemetry::HISTOGRAM_FLAG || type == nsITelemetry::HISTOGRAM_COUNT) { Histogram *h; mozilla::DebugOnly<nsresult> rv = internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h); MOZ_ASSERT(NS_SUCCEEDED(rv)); - rv = internal_GetHistogramByEnumId(mozilla::Telemetry::ID(i), &h, true); - MOZ_ASSERT(NS_SUCCEEDED(rv)); } } StatisticsRecorder::Histograms hs; StatisticsRecorder::GetHistograms(&hs); // We identify corrupt histograms first, rather than interspersing it // in the loop below, to ensure that our corruption statistics don't @@ -2604,48 +2358,8 @@ TelemetryHistogram::GetHistogramSizesofI StatisticsRecorder::GetHistograms(&hs); size_t n = 0; for (HistogramIterator it = hs.begin(); it != hs.end(); ++it) { Histogram *h = *it; n += h->SizeOfIncludingThis(aMallocSizeOf); } return n; } - -// This method takes the lock only to double-buffer the batched telemetry. -// It releases the lock before calling out to IPC code which can (and does) -// Accumulate (which would deadlock) -// -// To ensure we don't loop IPCTimerFired->AccumulateChild->arm timer, we don't -// unset gIPCTimerArmed until the IPC completes -// -// This function must be called on the main thread, otherwise IPC will fail. -void -TelemetryHistogram::IPCTimerFired(nsITimer* aTimer, void* aClosure) -{ - MOZ_ASSERT(NS_IsMainThread()); - nsTArray<Accumulation> accumulationsToSend; - nsTArray<KeyedAccumulation> keyedAccumulationsToSend; - { - StaticMutexAutoLock locker(gTelemetryHistogramMutex); - if (gAccumulations) { - accumulationsToSend.SwapElements(*gAccumulations); - } - if (gKeyedAccumulations) { - keyedAccumulationsToSend.SwapElements(*gKeyedAccumulations); - } - } - - mozilla::dom::ContentChild* contentChild = mozilla::dom::ContentChild::GetSingleton(); - mozilla::Unused << NS_WARN_IF(!contentChild); - if (contentChild) { - if (accumulationsToSend.Length()) { - mozilla::Unused << - NS_WARN_IF(!contentChild->SendAccumulateChildHistogram(accumulationsToSend)); - } - if (keyedAccumulationsToSend.Length()) { - mozilla::Unused << - NS_WARN_IF(!contentChild->SendAccumulateChildKeyedHistogram(keyedAccumulationsToSend)); - } - } - - gIPCTimerArmed = false; -}
--- a/toolkit/components/telemetry/TelemetryHistogram.h +++ b/toolkit/components/telemetry/TelemetryHistogram.h @@ -3,18 +3,16 @@ * 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 TelemetryHistogram_h__ #define TelemetryHistogram_h__ #include "mozilla/TelemetryHistogramEnums.h" -#include "mozilla/TelemetryComms.h" - // 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. // For the public interface to Telemetry functionality, see Telemetry.h. namespace TelemetryHistogram { void CreateStatisticsRecorder(); @@ -39,19 +37,16 @@ nsresult SetHistogramRecordingEnabled(co void Accumulate(mozilla::Telemetry::ID aHistogram, uint32_t aSample); void Accumulate(mozilla::Telemetry::ID aID, const nsCString& aKey, uint32_t aSample); void Accumulate(const char* name, uint32_t sample); void Accumulate(const char* name, const nsCString& key, uint32_t sample); void AccumulateCategorical(mozilla::Telemetry::ID aId, const nsCString& aLabel); -void AccumulateChild(const nsTArray<mozilla::Telemetry::Accumulation>& aAccumulations); -void AccumulateChildKeyed(const nsTArray<mozilla::Telemetry::KeyedAccumulation>& aAccumulations); - void ClearHistogram(mozilla::Telemetry::ID aId); nsresult GetHistogramById(const nsACString &name, JSContext *cx, JS::MutableHandle<JS::Value> ret); nsresult @@ -102,13 +97,11 @@ nsresult GetAddonHistogramSnapshots(JSContext *cx, JS::MutableHandle<JS::Value> ret); size_t GetMapShallowSizesOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf); size_t GetHistogramSizesofIncludingThis(mozilla::MallocSizeOf aMallocSizeOf); -void -IPCTimerFired(nsITimer* aTimer, void* aClosure); } // namespace TelemetryHistogram #endif // TelemetryHistogram_h__
--- a/toolkit/components/telemetry/TelemetrySession.jsm +++ b/toolkit/components/telemetry/TelemetrySession.jsm @@ -36,37 +36,33 @@ const REASON_ABORTED_SESSION = "aborted- const REASON_DAILY = "daily"; const REASON_SAVED_SESSION = "saved-session"; const REASON_GATHER_PAYLOAD = "gather-payload"; const REASON_GATHER_SUBSESSION_PAYLOAD = "gather-subsession-payload"; const REASON_TEST_PING = "test-ping"; const REASON_ENVIRONMENT_CHANGE = "environment-change"; const REASON_SHUTDOWN = "shutdown"; -const HISTOGRAM_SUFFIXES = { - PARENT: "", - CONTENT: "#content", -} - const ENVIRONMENT_CHANGE_LISTENER = "TelemetrySession::onEnvironmentChange"; const MS_IN_ONE_HOUR = 60 * 60 * 1000; const MIN_SUBSESSION_LENGTH_MS = Preferences.get("toolkit.telemetry.minSubsessionLength", 10 * 60) * 1000; const LOGGER_NAME = "Toolkit.Telemetry"; const LOGGER_PREFIX = "TelemetrySession" + (Utils.isContentProcess ? "#content::" : "::"); const PREF_BRANCH = "toolkit.telemetry."; const PREF_PREVIOUS_BUILDID = PREF_BRANCH + "previousBuildID"; const PREF_FHR_UPLOAD_ENABLED = "datareporting.healthreport.uploadEnabled"; const PREF_ASYNC_PLUGIN_INIT = "dom.ipc.plugins.asyncInit.enabled"; const PREF_UNIFIED = PREF_BRANCH + "unified"; const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload"; +const MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD = "Telemetry:GetChildPayload"; const MESSAGE_TELEMETRY_THREAD_HANGS = "Telemetry:ChildThreadHangs"; const MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS = "Telemetry:GetChildThreadHangs"; const MESSAGE_TELEMETRY_USS = "Telemetry:USS"; const MESSAGE_TELEMETRY_GET_CHILD_USS = "Telemetry:GetChildUSS"; const DATAREPORTING_DIRECTORY = "datareporting"; const ABORTED_SESSION_FILE_NAME = "aborted-session-ping"; @@ -540,16 +536,23 @@ this.TelemetrySession = Object.freeze({ * @param reason Optional, the reason to trigger the payload. * @param clearSubsession Optional, whether to clear subsession specific data. * @returns Object */ getPayload: function(reason, clearSubsession = false) { return Impl.getPayload(reason, clearSubsession); }, /** + * Asks the content processes to send their payloads. + * @returns Object + */ + requestChildPayloads: function() { + return Impl.requestChildPayloads(); + }, + /** * Returns a promise that resolves to an array of thread hang stats from content processes, one entry per process. * The structure of each entry is identical to that of "threadHangStats" in nsITelemetry. * While thread hang stats are also part of the child payloads, this function is useful for cheaply getting this information, * which is useful for realtime hang monitoring. * Child processes that do not respond, or spawn/die during execution of this function are excluded from the result. * @returns Promise */ getChildThreadHangs: function() { @@ -876,38 +879,33 @@ var Impl = { * @return {Integer} A value from nsITelemetry.DATASET_*. */ getDatasetType: function() { return Telemetry.canRecordExtended ? Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTIN : Ci.nsITelemetry.DATASET_RELEASE_CHANNEL_OPTOUT; }, getHistograms: function getHistograms(subsession, clearSubsession) { - this._log.trace("getHistograms - subsession: " + subsession + - ", clearSubsession: " + clearSubsession); + this._log.trace("getHistograms - subsession: " + subsession + ", clearSubsession: " + clearSubsession); let registered = Telemetry.registeredHistograms(this.getDatasetType(), []); - if (this._testing == false) { - // Omit telemetry test histograms outside of tests. - registered = registered.filter(n => !n.startsWith("TELEMETRY_TEST_")); - } - registered = registered.concat(registered.map(n => "STARTUP_" + n)); - let hls = subsession ? Telemetry.snapshotSubsessionHistograms(clearSubsession) : Telemetry.histogramSnapshots; let ret = {}; for (let name of registered) { - for (let suffix of Object.values(HISTOGRAM_SUFFIXES)) { - if (name + suffix in hls) { - if (!(suffix in ret)) { - ret[suffix] = {}; + for (let n of [name, "STARTUP_" + name]) { + if (n in hls) { + // Omit telemetry test histograms outside of tests. + if (n.startsWith('TELEMETRY_TEST_') && this._testing == false) { + this._log.trace("getHistograms - Skipping test histogram: " + n); + } else { + ret[n] = this.packHistogram(hls[n]); } - ret[suffix][name] = this.packHistogram(hls[name + suffix]); } } } return ret; }, getAddonHistograms: function getAddonHistograms() { @@ -925,51 +923,46 @@ var Impl = { if (Object.keys(packedHistograms).length != 0) ret[addonName] = packedHistograms; } return ret; }, getKeyedHistograms: function(subsession, clearSubsession) { - this._log.trace("getKeyedHistograms - subsession: " + subsession + - ", clearSubsession: " + clearSubsession); + this._log.trace("getKeyedHistograms - subsession: " + subsession + ", clearSubsession: " + clearSubsession); let registered = Telemetry.registeredKeyedHistograms(this.getDatasetType(), []); - if (this._testing == false) { - // Omit telemetry test histograms outside of tests. - registered = registered.filter(id => !id.startsWith("TELEMETRY_TEST_")); - } let ret = {}; for (let id of registered) { - for (let suffix of Object.values(HISTOGRAM_SUFFIXES)) { - let keyed = Telemetry.getKeyedHistogramById(id + suffix); - let snapshot = null; - if (subsession) { - snapshot = clearSubsession ? keyed.snapshotSubsessionAndClear() - : keyed.subsessionSnapshot(); - } else { - snapshot = keyed.snapshot(); - } + // Omit telemetry test histograms outside of tests. + if (id.startsWith('TELEMETRY_TEST_') && this._testing == false) { + this._log.trace("getKeyedHistograms - Skipping test histogram: " + id); + continue; + } + let keyed = Telemetry.getKeyedHistogramById(id); + let snapshot = null; + if (subsession) { + snapshot = clearSubsession ? keyed.snapshotSubsessionAndClear() + : keyed.subsessionSnapshot(); + } else { + snapshot = keyed.snapshot(); + } - let keys = Object.keys(snapshot); - if (keys.length == 0) { - // Skip empty keyed histogram. - continue; - } + let keys = Object.keys(snapshot); + if (keys.length == 0) { + // Skip empty keyed histogram. + continue; + } - if (!(suffix in ret)) { - ret[suffix] = {}; - } - ret[suffix][id] = {}; - for (let key of keys) { - ret[suffix][id][key] = this.packHistogram(snapshot[key]); - } + ret[id] = {}; + for (let key of keys) { + ret[id][key] = this.packHistogram(snapshot[key]); } } return ret; }, getScalars: function (subsession, clearSubsession) { this._log.trace("getScalars - subsession: " + subsession + ", clearSubsession: " + clearSubsession); @@ -1245,58 +1238,53 @@ var Impl = { assemblePayloadWithMeasurements: function(simpleMeasurements, info, reason, clearSubsession) { const isSubsession = IS_UNIFIED_TELEMETRY && !this._isClassicReason(reason); clearSubsession = IS_UNIFIED_TELEMETRY && clearSubsession; this._log.trace("assemblePayloadWithMeasurements - reason: " + reason + ", submitting subsession data: " + isSubsession); // This allows wrapping data retrieval calls in a try-catch block so that // failures don't break the rest of the ping assembly. - const protect = (fn, defaultReturn = null) => { + const protect = (fn) => { try { return fn(); } catch (ex) { this._log.error("assemblePayloadWithMeasurements - caught exception", ex); - return defaultReturn; + return null; } }; // Payload common to chrome and content processes. let payloadObj = { ver: PAYLOAD_VERSION, simpleMeasurements: simpleMeasurements, + histograms: protect(() => this.getHistograms(isSubsession, clearSubsession)), + keyedHistograms: protect(() => this.getKeyedHistograms(isSubsession, clearSubsession)), }; // Add extended set measurements common to chrome & content processes if (Telemetry.canRecordExtended) { payloadObj.chromeHangs = protect(() => Telemetry.chromeHangs); payloadObj.threadHangStats = protect(() => this.getThreadHangStats(Telemetry.threadHangStats)); payloadObj.log = protect(() => TelemetryLog.entries()); payloadObj.webrtc = protect(() => Telemetry.webrtcStats); } if (Utils.isContentProcess) { return payloadObj; } - // Additional payload for chrome process. - let histograms = protect(() => this.getHistograms(isSubsession, clearSubsession), {}); - let keyedHistograms = protect(() => this.getKeyedHistograms(isSubsession, clearSubsession), {}); - payloadObj.histograms = histograms[HISTOGRAM_SUFFIXES.PARENT] || {}; - payloadObj.keyedHistograms = keyedHistograms[HISTOGRAM_SUFFIXES.PARENT] || {}; + // Set the scalars for the parent process. payloadObj.processes = { parent: { scalars: protect(() => this.getScalars(isSubsession, clearSubsession)), - }, - content: { - histograms: histograms[HISTOGRAM_SUFFIXES.CONTENT], - keyedHistograms: keyedHistograms[HISTOGRAM_SUFFIXES.CONTENT], - }, + } }; + // Additional payload for chrome process. payloadObj.info = info; // Add extended set measurements for chrome process. if (Telemetry.canRecordExtended) { payloadObj.slowSQL = protect(() => Telemetry.slowSQL); payloadObj.fileIOReports = protect(() => Telemetry.fileIOReports); payloadObj.lateWrites = protect(() => Telemetry.lateWrites); @@ -1528,16 +1516,17 @@ var Impl = { this._testing = testing; if (!Telemetry.canRecordBase) { this._log.trace("setupContentProcess - base recording is disabled, not initializing"); return; } Services.obs.addObserver(this, "content-child-shutdown", false); + cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD, this); cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_THREAD_HANGS, this); cpml.addMessageListener(MESSAGE_TELEMETRY_GET_CHILD_USS, this); this.gatherStartupHistograms(); let delayedTask = new DeferredTask(function* () { this._initialized = true; @@ -1565,28 +1554,42 @@ var Impl = { this._log.trace("receiveMessage - Message name " + message.name); switch (message.name) { case MESSAGE_TELEMETRY_PAYLOAD: { // In parent process, receive Telemetry payload from child let source = message.data.childUUID; delete message.data.childUUID; + for (let child of this._childTelemetry) { + if (child.source === source) { + // Update existing telemetry data. + child.payload = message.data; + return; + } + } + // Did not find existing child in this._childTelemetry. this._childTelemetry.push({ source: source, payload: message.data, }); if (this._childTelemetry.length == MAX_NUM_CONTENT_PAYLOADS + 1) { this._childTelemetry.shift(); Telemetry.getHistogramById("TELEMETRY_DISCARDED_CONTENT_PINGS_COUNT").add(); } break; } + case MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD: + { + // In child process, send the requested Telemetry payload + this.sendContentProcessPing("saved-session"); + break; + } case MESSAGE_TELEMETRY_THREAD_HANGS: { // Accumulate child thread hang stats from this child this._childThreadHangs.push(message.data); // Check if we've got data from all the children, accounting for child processes dying // if it happens before the last response is received and no new child processes are spawned at the exact same time // If that happens, we can resolve the promise earlier rather than having to wait for the timeout to expire @@ -1756,16 +1759,21 @@ var Impl = { if (Object.keys(this._slowSQLStartup).length == 0) { this.gatherStartupHistograms(); this._slowSQLStartup = Telemetry.slowSQL; } this.gatherMemory(); return this.getSessionPayload(reason, clearSubsession); }, + requestChildPayloads: function() { + this._log.trace("requestChildPayloads"); + ppmm.broadcastAsyncMessage(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD, {}); + }, + getChildThreadHangs: function getChildThreadHangs() { return new Promise((resolve) => { // Return immediately if there are no child processes to get stats from if (ppmm.childCount === 0) { resolve([]); return; } @@ -1833,17 +1841,17 @@ var Impl = { this._log.trace("observe - " + aTopic + " notified."); } switch (aTopic) { case "content-child-shutdown": // content-child-shutdown is only registered for content processes. Services.obs.removeObserver(this, "content-child-shutdown"); this.uninstall(); - Telemetry.flushBatchedChildTelemetry(); + this.sendContentProcessPing(REASON_SAVED_SESSION); break; case TOPIC_CYCLE_COLLECTOR_BEGIN: let now = new Date(); if (!gLastMemoryPoll || (TELEMETRY_INTERVAL <= now - gLastMemoryPoll)) { gLastMemoryPoll = now; this.gatherMemory();
--- a/toolkit/components/telemetry/moz.build +++ b/toolkit/components/telemetry/moz.build @@ -14,17 +14,16 @@ XPIDL_SOURCES += [ XPIDL_MODULE = 'telemetry' EXPORTS.mozilla += [ '!TelemetryHistogramEnums.h', '!TelemetryScalarEnums.h', 'ProcessedStack.h', 'Telemetry.h', - 'TelemetryComms.h', 'ThreadHangStats.h', ] SOURCES += [ 'Telemetry.cpp', 'TelemetryCommon.cpp', 'TelemetryHistogram.cpp', 'TelemetryScalar.cpp',
--- a/toolkit/components/telemetry/nsITelemetry.idl +++ b/toolkit/components/telemetry/nsITelemetry.idl @@ -411,15 +411,9 @@ interface nsITelemetry : nsISupports */ [implicit_jscontext, optional_argc] jsval snapshotScalars(in uint32_t aDataset, [optional] in boolean aClear); /** * Resets all the stored scalars. This is intended to be only used in tests. */ void clearScalars(); - - /** - * Immediately sends any Telemetry batched on this process to the parent - * process. This is intended only to be used on process shutdown. - */ - void flushBatchedChildTelemetry(); };
--- a/toolkit/components/telemetry/tests/unit/test_ChildHistograms.js +++ b/toolkit/components/telemetry/tests/unit/test_ChildHistograms.js @@ -1,13 +1,12 @@ Cu.import("resource://gre/modules/TelemetryController.jsm", this); Cu.import("resource://gre/modules/TelemetrySession.jsm", this); Cu.import("resource://gre/modules/PromiseUtils.jsm", this); -Cu.import("resource://testing-common/ContentTaskUtils.jsm", this); const MESSAGE_TELEMETRY_PAYLOAD = "Telemetry:Payload"; const MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD = "Telemetry:GetChildPayload"; const MESSAGE_CHILD_TEST_DONE = "ChildTest:Done"; const PLATFORM_VERSION = "1.9.2"; const APP_VERSION = "1"; const APP_ID = "xpcshell@tests.mozilla.org"; @@ -15,40 +14,38 @@ const APP_NAME = "XPCShell"; function run_child_test() { // Setup histograms with some fixed values. let flagHist = Telemetry.getHistogramById("TELEMETRY_TEST_FLAG"); flagHist.add(1); let countHist = Telemetry.getHistogramById("TELEMETRY_TEST_COUNT"); countHist.add(); countHist.add(); - let categHist = Telemetry.getHistogramById("TELEMETRY_TEST_CATEGORICAL"); - categHist.add("Label2"); - categHist.add("Label3"); let flagKeyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_FLAG"); flagKeyed.add("a", 1); flagKeyed.add("b", 1); let countKeyed = Telemetry.getKeyedHistogramById("TELEMETRY_TEST_KEYED_COUNT"); countKeyed.add("a"); countKeyed.add("b"); countKeyed.add("b"); + + // Check payload values. + const payload = TelemetrySession.getPayload("test-ping"); + check_histogram_values(payload); } function check_histogram_values(payload) { const hs = payload.histograms; Assert.ok("TELEMETRY_TEST_COUNT" in hs, "Should have count test histogram."); Assert.ok("TELEMETRY_TEST_FLAG" in hs, "Should have flag test histogram."); - Assert.ok("TELEMETRY_TEST_CATEGORICAL" in hs, "Should have categorical test histogram."); Assert.equal(hs["TELEMETRY_TEST_COUNT"].sum, 2, "Count test histogram should have the right value."); Assert.equal(hs["TELEMETRY_TEST_FLAG"].sum, 1, "Flag test histogram should have the right value."); - Assert.equal(hs["TELEMETRY_TEST_CATEGORICAL"].sum, 3, - "Categorical test histogram should have the right sum."); const kh = payload.keyedHistograms; Assert.ok("TELEMETRY_TEST_KEYED_COUNT" in kh, "Should have keyed count test histogram."); Assert.ok("TELEMETRY_TEST_KEYED_FLAG" in kh, "Should have keyed flag test histogram."); Assert.equal(kh["TELEMETRY_TEST_KEYED_COUNT"]["a"].sum, 1, "Keyed count test histogram should have the right value."); Assert.equal(kh["TELEMETRY_TEST_KEYED_COUNT"]["b"].sum, 2, "Keyed count test histogram should have the right value."); @@ -59,16 +56,18 @@ function check_histogram_values(payload) } add_task(function*() { if (!runningInParent) { TelemetryController.testSetupContent(); run_child_test(); dump("... done with child test\n"); do_send_remote_message(MESSAGE_CHILD_TEST_DONE); + dump("... waiting for child payload collection\n"); + yield do_await_remote_message(MESSAGE_TELEMETRY_GET_CHILD_PAYLOAD); return; } // Setup. do_get_profile(true); loadAddonManager(APP_ID, APP_NAME, APP_VERSION, PLATFORM_VERSION); Services.prefs.setBoolPref(PREF_TELEMETRY_ENABLED, true); yield TelemetryController.testSetup(); @@ -76,25 +75,25 @@ add_task(function*() { // Make sure we don't generate unexpected pings due to pref changes. yield setEmptyPrefWatchlist(); } // Run test in child, don't wait for it to finish. let childPromise = run_test_in_child("test_ChildHistograms.js"); yield do_await_remote_message(MESSAGE_CHILD_TEST_DONE); - yield ContentTaskUtils.waitForCondition(() => { - let payload = TelemetrySession.getPayload("test-ping"); - return payload && - "processes" in payload && - "content" in payload.processes && - "histograms" in payload.processes.content && - "TELEMETRY_TEST_COUNT" in payload.processes.content.histograms; - }); + // Gather payload from child. + dump("... requesting child payloads\n"); + let promiseMessage = do_await_remote_message(MESSAGE_TELEMETRY_PAYLOAD); + TelemetrySession.requestChildPayloads(); + yield promiseMessage; + dump("... received child payload\n"); + + // Check child payload. const payload = TelemetrySession.getPayload("test-ping"); - Assert.ok("processes" in payload, "Should have processes section"); - Assert.ok("content" in payload.processes, "Should have child process section"); - Assert.ok("histograms" in payload.processes.content, "Child process section should have histograms."); - Assert.ok("keyedHistograms" in payload.processes.content, "Child process section should have keyed histograms."); - check_histogram_values(payload.processes.content); + Assert.ok("childPayloads" in payload, "Should have child payloads."); + Assert.equal(payload.childPayloads.length, 1, "Should have received one child payload so far."); + Assert.ok("histograms" in payload.childPayloads[0], "Child payload should have histograms."); + Assert.ok("keyedHistograms" in payload.childPayloads[0], "Child payload should have keyed histograms."); + check_histogram_values(payload.childPayloads[0]); do_test_finished(); });
--- a/toolkit/content/aboutTelemetry.css +++ b/toolkit/content/aboutTelemetry.css @@ -224,24 +224,16 @@ body[dir="rtl"] .copy-node { padding-inline-start: 10em; display: none; } .has-data.expanded .filter-ui { display: inline; } -.processes-ui { - display: none; -} - -.has-data.expanded .processes-ui { - display: initial; -} - .filter-blocked { display: none; } #raw-ping-data-section { width: 100%; height: 100%; background-color:-moz-Dialog; @@ -260,12 +252,8 @@ body[dir="rtl"] .copy-node { padding: 5px 10px; } /* addon subsection style */ .addon-caption { font-size: larger; margin: 5px 0; } - -.process-picker { - margin: 0 0.5em; -}
--- a/toolkit/content/aboutTelemetry.js +++ b/toolkit/content/aboutTelemetry.js @@ -301,20 +301,16 @@ var PingPicker = { }, false); document.getElementById("newer-ping") .addEventListener("click", () => this._movePingIndex(-1), false); document.getElementById("older-ping") .addEventListener("click", () => this._movePingIndex(1), false); document.getElementById("choose-payload") .addEventListener("change", () => displayPingData(gPingData), false); - document.getElementById("histograms-processes") - .addEventListener("change", () => displayPingData(gPingData), false); - document.getElementById("keyed-histograms-processes") - .addEventListener("change", () => displayPingData(gPingData), false); }, onPingSourceChanged: function() { this.update(); }, onPingDisplayChanged: function() { this.update(); @@ -1781,43 +1777,16 @@ function sortStartupMilestones(aSimpleMe let result = {}; for (let key of sortedKeys) { result[key] = aSimpleMeasurements[key]; } return result; } -function renderProcessList(ping, selectEl) { - removeAllChildNodes(selectEl); - let option = document.createElement("option"); - option.appendChild(document.createTextNode("parent")); - option.setAttribute("value", ""); - option.selected = true; - selectEl.appendChild(option); - - if (!("processes" in ping.payload)) { - selectEl.disabled = true; - return; - } - selectEl.disabled = false; - - for (let process of Object.keys(ping.payload.processes)) { - // TODO: parent hgrams are on root payload, not in payload.processes.parent - // When/If that gets moved, you'll need to remove this: - if (process === "parent") { - continue; - } - option = document.createElement("option"); - option.appendChild(document.createTextNode(process)); - option.setAttribute("value", process); - selectEl.appendChild(option); - } -} - function renderPayloadList(ping) { // Rebuild the payload select with options: // Parent Payload (selected) // Child Payload 1..ping.payload.childPayloads.length let listEl = document.getElementById("choose-payload"); removeAllChildNodes(listEl); let option = document.createElement("option"); @@ -1876,21 +1845,19 @@ function displayPingData(ping, updatePay // Render raw ping data. let pre = document.getElementById("raw-ping-data"); pre.textContent = JSON.stringify(gPingData, null, 2); // Update the structured data rendering. const keysHeader = bundle.GetStringFromName("keysHeader"); const valuesHeader = bundle.GetStringFromName("valuesHeader"); - // Update the payload list and process lists + // Update the payload list if (updatePayloadList) { renderPayloadList(ping); - renderProcessList(ping, document.getElementById("histograms-processes")); - renderProcessList(ping, document.getElementById("keyed-histograms-processes")); } // Show general data. GeneralData.render(ping); // Show environment data. EnvironmentData.render(ping); @@ -1957,28 +1924,18 @@ function displayPingData(ping, updatePay // Show scalar data. Scalars.render(payload); // Show histogram data let hgramDiv = document.getElementById("histograms"); removeAllChildNodes(hgramDiv); let histograms = payload.histograms; - - let hgramsSelect = document.getElementById("histograms-processes"); - let hgramsOption = hgramsSelect.selectedOptions.item(0); - let hgramsProcess = hgramsOption.getAttribute("value"); - if (hgramsProcess && - "processes" in ping.payload && - hgramsProcess in ping.payload.processes) { - histograms = ping.payload.processes[hgramsProcess].histograms; - } - hasData = Object.keys(histograms).length > 0; - setHasData("histograms-section", hasData || hgramsSelect.options.length); + setHasData("histograms-section", hasData); if (hasData) { for (let [name, hgram] of Object.entries(histograms)) { Histogram.render(hgramDiv, name, hgram, {unpacked: true}); } let filterBox = document.getElementById("histograms-filter"); filterBox.addEventListener("input", Histogram.histogramFilterChanged, false); @@ -1988,37 +1945,27 @@ function displayPingData(ping, updatePay setHasData("histograms-section", true); } // Show keyed histogram data let keyedDiv = document.getElementById("keyed-histograms"); removeAllChildNodes(keyedDiv); + setHasData("keyed-histograms-section", false); let keyedHistograms = payload.keyedHistograms; - - let keyedHgramsSelect = document.getElementById("keyed-histograms-processes"); - let keyedHgramsOption = keyedHgramsSelect.selectedOptions.item(0); - let keyedHgramsProcess = keyedHgramsOption.getAttribute("value"); - if (keyedHgramsProcess && - "processes" in ping.payload && - keyedHgramsProcess in ping.payload.processes) { - keyedHistograms = ping.payload.processes[keyedHgramsProcess].keyedHistograms; - } - - setHasData("keyed-histograms-section", keyedHgramsSelect.options.length); if (keyedHistograms) { let hasData = false; for (let [id, keyed] of Object.entries(keyedHistograms)) { if (Object.keys(keyed).length > 0) { hasData = true; KeyedHistogram.render(keyedDiv, id, keyed, {unpacked: true}); } } - setHasData("keyed-histograms-section", hasData || keyedHgramsSelect.options.length); + setHasData("keyed-histograms-section", hasData); } // Show addon histogram data let addonDiv = document.getElementById("addon-histograms"); removeAllChildNodes(addonDiv); let addonHistogramsRendered = false; let addonData = payload.addonHistograms;
--- a/toolkit/content/aboutTelemetry.xhtml +++ b/toolkit/content/aboutTelemetry.xhtml @@ -147,31 +147,25 @@ <section id="histograms-section" class="data-section"> <input type="checkbox" class="statebox"/> <h1 class="section-name">&aboutTelemetry.histogramsSection;</h1> <span class="toggle-caption">&aboutTelemetry.toggle;</span> <span class="empty-caption">&aboutTelemetry.emptySection;</span> <span class="filter-ui"> &aboutTelemetry.filterText; <input type="text" class="filter" id="histograms-filter" target_id="histograms"/> </span> - <div class="processes-ui"> - <select id="histograms-processes" class="process-picker"></select> - </div> <div id="histograms" class="data"> </div> </section> <section id="keyed-histograms-section" class="data-section"> <input type="checkbox" class="statebox"/> <h1 class="section-name">&aboutTelemetry.keyedHistogramsSection;</h1> <span class="toggle-caption">&aboutTelemetry.toggle;</span> <span class="empty-caption">&aboutTelemetry.emptySection;</span> - <div class="processes-ui"> - <select id="keyed-histograms-processes" class="process-picker"></select> - </div> <div id="keyed-histograms" class="data"> </div> </section> <section id="simple-measurements-section" class="data-section"> <input type="checkbox" class="statebox"/> <h1 class="section-name">&aboutTelemetry.simpleMeasurementsSection;</h1> <span class="toggle-caption">&aboutTelemetry.toggle;</span>