diff --git a/dom/base/ChromeUtils.cpp b/dom/base/ChromeUtils.cpp index 39f99cd9a6cf..520c37723e6e 100644 --- a/dom/base/ChromeUtils.cpp +++ b/dom/base/ChromeUtils.cpp @@ -12,7 +12,7 @@ #include "mozilla/Base64.h" #include "mozilla/BasePrincipal.h" #include "mozilla/CycleCollectedJSRuntime.h" -#include "mozilla/PerformanceMetricsCollector.h" +#include "mozilla/PerformanceUtils.h" #include "mozilla/Preferences.h" #include "mozilla/TimeStamp.h" #include "mozilla/dom/ContentParent.h" @@ -655,27 +655,29 @@ ChromeUtils::ClearRecentJSDevError(GlobalObject&) } #endif // NIGHTLY_BUILD -/* static */ -already_AddRefed -ChromeUtils::RequestPerformanceMetrics(GlobalObject& aGlobal, - ErrorResult& aRv) +/* static */ void +ChromeUtils::RequestPerformanceMetrics(GlobalObject&) { MOZ_ASSERT(XRE_IsParentProcess()); - // Creating a promise - nsCOMPtr global = do_QueryInterface(aGlobal.GetAsSupports()); - MOZ_ASSERT(global); - RefPtr domPromise = Promise::Create(global, aRv); - if (NS_WARN_IF(aRv.Failed())) { - return nullptr; + // calling all content processes via IPDL (async) + nsTArray children; + ContentParent::GetAll(children); + for (uint32_t i = 0; i < children.Length(); i++) { + mozilla::Unused << children[i]->SendRequestPerformanceMetrics(); } - MOZ_ASSERT(domPromise); - // requesting metrics, that will be returned into the promise - PerformanceMetricsCollector::RequestMetrics(domPromise); - // sending back the promise instance - return domPromise.forget(); + // collecting the current process counters and notifying them + nsTArray info; + CollectPerformanceInfo(info); + SystemGroup::Dispatch(TaskCategory::Performance, + NS_NewRunnableFunction( + "RequestPerformanceMetrics", + [info]() { mozilla::Unused << NS_WARN_IF(NS_FAILED(NotifyPerformanceInfo(info))); } + ) + ); + } constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude; diff --git a/dom/base/ChromeUtils.h b/dom/base/ChromeUtils.h index cbc7fe244c16..44a87d5ea7d8 100644 --- a/dom/base/ChromeUtils.h +++ b/dom/base/ChromeUtils.h @@ -160,9 +160,7 @@ public: static void ClearRecentJSDevError(GlobalObject& aGlobal); - static already_AddRefed - RequestPerformanceMetrics(GlobalObject& aGlobal, - ErrorResult& aRv); + static void RequestPerformanceMetrics(GlobalObject& aGlobal); static void Import(const GlobalObject& aGlobal, const nsAString& aResourceURI, diff --git a/dom/base/moz.build b/dom/base/moz.build index d8c5e938c5ed..2ada52e99e0c 100644 --- a/dom/base/moz.build +++ b/dom/base/moz.build @@ -22,6 +22,7 @@ XPIDL_SOURCES += [ 'nsIImageLoadingContent.idl', 'nsIMessageManager.idl', 'nsIObjectLoadingContent.idl', + 'nsIPerformanceMetrics.idl', 'nsIRemoteWindowContext.idl', 'nsIScriptChannel.idl', 'nsISelectionController.idl', @@ -97,6 +98,7 @@ EXPORTS += [ 'nsNameSpaceManager.h', 'nsNodeInfoManager.h', 'nsNodeUtils.h', + 'nsPerformanceMetrics.h', 'nsPIDOMWindow.h', 'nsPIDOMWindowInlines.h', 'nsPIWindowRoot.h', @@ -327,6 +329,7 @@ UNIFIED_SOURCES += [ 'nsNodeInfoManager.cpp', 'nsNodeUtils.cpp', 'nsOpenURIInFrameParams.cpp', + 'nsPerformanceMetrics.cpp', 'nsPlainTextSerializer.cpp', 'nsPropertyTable.cpp', 'nsQueryContentEventResult.cpp', diff --git a/dom/base/nsIPerformanceMetrics.idl b/dom/base/nsIPerformanceMetrics.idl new file mode 100644 index 000000000000..2cbaa435a48d --- /dev/null +++ b/dom/base/nsIPerformanceMetrics.idl @@ -0,0 +1,49 @@ +/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*-*/ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "nsISupports.idl" +#include "nsIArray.idl" + +/* + * nsIPerformanceMetricsData is used to store performance data collected + * in all content processes by nsThread and nsWorkerThread. + * + * Each (host, category, pid, wid, pwid) is unique to a given DocGroup or + * Worker, and we collect the number of dispatches and execution duration. + * + * This XPCOM interface reflects the data collected in Performance counters. + * see xpcom/threads/PerformanceCounter.h + */ +[scriptable, builtinclass, uuid(1f9a58c9-be37-4463-8996-c7f5b9a5bef8)] +interface nsIPerformanceMetricsDispatchCategory : nsISupports +{ + // DispatchCategory value + readonly attribute unsigned long category; + // Number of dispatch. + readonly attribute unsigned long count; +}; + + +[scriptable, builtinclass, uuid(02b0cdc6-4be2-4154-a8a9-e8d462073200)] +interface nsIPerformanceMetricsData : nsISupports +{ + // Host of the document, if any + readonly attribute AUTF8String host; + // process id + readonly attribute unsigned long pid; + // window id + readonly attribute unsigned long long wid; + // "parent" window id + readonly attribute unsigned long long pwid; + // Execution time in microseconds + readonly attribute unsigned long long duration; + // True if the data is collected in a worker + readonly attribute bool worker; + // Dispatch Category counters + readonly attribute nsIArray items; +}; + + diff --git a/dom/base/nsPerformanceMetrics.cpp b/dom/base/nsPerformanceMetrics.cpp new file mode 100644 index 000000000000..4c1a1174d797 --- /dev/null +++ b/dom/base/nsPerformanceMetrics.cpp @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 8; 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 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include +#include +#include +#include "nsComponentManagerUtils.h" + +/* ------------------------------------------------------ + * + * class PerformanceMetricsDispatchCategory + * + */ + +PerformanceMetricsDispatchCategory::PerformanceMetricsDispatchCategory(uint32_t aCategory, uint32_t aCount) + : mCategory(aCategory), mCount(aCount) +{ +} + +NS_IMPL_ISUPPORTS(PerformanceMetricsDispatchCategory, + nsIPerformanceMetricsDispatchCategory); + + +NS_IMETHODIMP +PerformanceMetricsDispatchCategory::GetCategory(uint32_t* aCategory) +{ + *aCategory = mCategory; + return NS_OK; +}; + +NS_IMETHODIMP +PerformanceMetricsDispatchCategory::GetCount(uint32_t* aCount) +{ + *aCount = mCount; + return NS_OK; +}; + +/* ------------------------------------------------------ + * + * class PerformanceMetricsData + * + */ + +PerformanceMetricsData::PerformanceMetricsData(uint32_t aPid, uint64_t aWid, + uint64_t aPwid, const nsCString& aHost, + uint64_t aDuration, bool aWorker, + nsIArray* aItems) + : mPid(aPid), mWid(aWid), mPwid(aPwid), mHost(aHost) + , mDuration(aDuration), mWorker(aWorker) +{ + uint32_t len; + nsresult rv = aItems->GetLength(&len); + if (NS_FAILED(rv)) { + NS_ASSERTION(rv == NS_OK, "Failed to ge the length"); + } + for (uint32_t i = 0; i < len; i++) { + nsCOMPtr item = do_QueryElementAt(aItems, i); + mItems.AppendElement(item); + } +}; + +NS_IMPL_ISUPPORTS(PerformanceMetricsData, nsIPerformanceMetricsData); + +NS_IMETHODIMP +PerformanceMetricsData::GetHost(nsACString& aHost) +{ + aHost = mHost; + return NS_OK; +}; + +NS_IMETHODIMP +PerformanceMetricsData::GetWorker(bool* aWorker) +{ + *aWorker = mWorker; + return NS_OK; +}; + +NS_IMETHODIMP +PerformanceMetricsData::GetPid(uint32_t* aPid) +{ + *aPid = mPid; + return NS_OK; +}; + +NS_IMETHODIMP +PerformanceMetricsData::GetWid(uint64_t* aWid) +{ + *aWid = mWid; + return NS_OK; +}; + +NS_IMETHODIMP +PerformanceMetricsData::GetDuration(uint64_t* aDuration) +{ + *aDuration = mDuration; + return NS_OK; +}; + +NS_IMETHODIMP +PerformanceMetricsData::GetPwid(uint64_t* aPwid) +{ + *aPwid = mPwid; + return NS_OK; +}; + +NS_IMETHODIMP +PerformanceMetricsData::GetItems(nsIArray** aItems) +{ + NS_ENSURE_ARG_POINTER(aItems); + *aItems = nullptr; + + nsresult rv = NS_OK; + nsCOMPtr items = + do_CreateInstance(NS_ARRAY_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + uint32_t len = mItems.Length(); + for (uint32_t i = 0; i < len; i++) { + items->AppendElement(mItems[i]); + } + + items.forget(aItems); + return NS_OK; +} diff --git a/dom/base/nsPerformanceMetrics.h b/dom/base/nsPerformanceMetrics.h new file mode 100644 index 000000000000..42088487822b --- /dev/null +++ b/dom/base/nsPerformanceMetrics.h @@ -0,0 +1,48 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef nsPerformanceMetrics_h___ +#define nsPerformanceMetrics_h___ + +#include "nsCOMArray.h" +#include "nsIPerformanceMetrics.h" +#include "nsString.h" + + +class PerformanceMetricsDispatchCategory final : public nsIPerformanceMetricsDispatchCategory +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPERFORMANCEMETRICSDISPATCHCATEGORY + PerformanceMetricsDispatchCategory(uint32_t aCategory, uint32_t aCount); +private: + ~PerformanceMetricsDispatchCategory() = default; + + uint32_t mCategory; + uint32_t mCount; +}; + + +class PerformanceMetricsData final : public nsIPerformanceMetricsData +{ +public: + NS_DECL_ISUPPORTS + NS_DECL_NSIPERFORMANCEMETRICSDATA + PerformanceMetricsData(uint32_t aPid, uint64_t aWid, uint64_t aPwid, const nsCString& aHost, + uint64_t aDuration, bool aWorker, nsIArray* aItems); +private: + ~PerformanceMetricsData() = default; + + uint32_t mPid; + uint64_t mWid; + uint64_t mPwid; + nsCString mHost; + uint64_t mDuration; + bool mWorker; + nsCOMArray mItems; +}; + +#endif // end nsPerformanceMetrics_h__ diff --git a/dom/chrome-webidl/ChromeUtils.webidl b/dom/chrome-webidl/ChromeUtils.webidl index 0cbf44508f7f..b40cb7b0e75a 100644 --- a/dom/chrome-webidl/ChromeUtils.webidl +++ b/dom/chrome-webidl/ChromeUtils.webidl @@ -345,10 +345,9 @@ partial namespace ChromeUtils { object createError(DOMString message, optional object? stack = null); /** - * Request performance metrics to the current process & all content processes. + * Request performance metrics to the current process & all ontent processes. */ - [Throws] - Promise> requestPerformanceMetrics(); + void requestPerformanceMetrics(); /** * Returns a Promise containing a sequence of I/O activities @@ -357,25 +356,6 @@ partial namespace ChromeUtils { Promise> requestIOActivity(); }; -/** - * Dictionaries duplicating IPDL types in dom/ipc/DOMTypes.ipdlh - * Used by requestPerformanceMetrics - */ -dictionary CategoryDispatchDictionary -{ - unsigned short category = 0; - unsigned short count = 0; -}; - -dictionary PerformanceInfoDictionary { - DOMString host = ""; - unsigned long pid = 0; - unsigned long long wid = 0; - unsigned long long pwid = 0; - unsigned long long duration = 0; - boolean worker = false; - sequence items = []; -}; /** * Used by requestIOActivity() to return the number of bytes diff --git a/dom/ipc/ContentChild.cpp b/dom/ipc/ContentChild.cpp index 18f12f8bef07..8d9f61fdf625 100644 --- a/dom/ipc/ContentChild.cpp +++ b/dom/ipc/ContentChild.cpp @@ -71,7 +71,6 @@ #include "mozilla/net/NeckoChild.h" #include "mozilla/net/CookieServiceChild.h" #include "mozilla/net/CaptivePortalService.h" -#include "mozilla/PerformanceMetricsCollector.h" #include "mozilla/PerformanceUtils.h" #include "mozilla/plugins/PluginInstanceParent.h" #include "mozilla/plugins/PluginModuleParent.h" @@ -1393,12 +1392,12 @@ ContentChild::GetResultForRenderingInitFailure(base::ProcessId aOtherPid) } mozilla::ipc::IPCResult -ContentChild::RecvRequestPerformanceMetrics(const nsID& aID) +ContentChild::RecvRequestPerformanceMetrics() { MOZ_ASSERT(mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()); nsTArray info; CollectPerformanceInfo(info); - SendAddPerformanceMetrics(aID, info); + SendAddPerformanceMetrics(info); return IPC_OK(); } diff --git a/dom/ipc/ContentChild.h b/dom/ipc/ContentChild.h index fa92912ef532..d25222a5c088 100644 --- a/dom/ipc/ContentChild.h +++ b/dom/ipc/ContentChild.h @@ -192,7 +192,7 @@ public: nsTArray&& namespaces) override; mozilla::ipc::IPCResult - RecvRequestPerformanceMetrics(const nsID& aID) override; + RecvRequestPerformanceMetrics() override; mozilla::ipc::IPCResult RecvReinitRendering( diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index af2ad64ebe4c..e5dc011e9109 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -188,7 +188,7 @@ #include "ContentProcessManager.h" #include "mozilla/dom/BlobURLProtocolHandler.h" #include "mozilla/dom/ipc/StructuredCloneData.h" -#include "mozilla/PerformanceMetricsCollector.h" +#include "mozilla/PerformanceUtils.h" #include "mozilla/psm/PSMContentListener.h" #include "nsPluginHost.h" #include "nsPluginTags.h" @@ -3339,16 +3339,14 @@ ContentParent::RecvFinishMemoryReport(const uint32_t& aGeneration) } mozilla::ipc::IPCResult -ContentParent::RecvAddPerformanceMetrics(const nsID& aID, - nsTArray&& aMetrics) +ContentParent::RecvAddPerformanceMetrics(nsTArray&& aMetrics) { if (!mozilla::StaticPrefs::dom_performance_enable_scheduler_timing()) { // The pref is off, we should not get a performance metrics from the content // child return IPC_OK(); } - nsresult rv = PerformanceMetricsCollector::DataReceived(aID, aMetrics); - Unused << NS_WARN_IF(NS_FAILED(rv)); + Unused << NS_WARN_IF(NS_FAILED(mozilla::NotifyPerformanceInfo(aMetrics))); return IPC_OK(); } diff --git a/dom/ipc/ContentParent.h b/dom/ipc/ContentParent.h index 3a660d62fe30..5882837f1c74 100644 --- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -856,7 +856,7 @@ private: mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override; mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override; - mozilla::ipc::IPCResult RecvAddPerformanceMetrics(const nsID& aID, nsTArray&& aMetrics) override; + mozilla::ipc::IPCResult RecvAddPerformanceMetrics(nsTArray&& aMetrics) override; virtual bool DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override; diff --git a/dom/ipc/PContent.ipdl b/dom/ipc/PContent.ipdl index df2390b8a20a..89485e0cca5f 100644 --- a/dom/ipc/PContent.ipdl +++ b/dom/ipc/PContent.ipdl @@ -408,7 +408,7 @@ child: bool anonymize, bool minimizeMemoryUsage, MaybeFileDesc DMDFile); - async RequestPerformanceMetrics(nsID aID); + async RequestPerformanceMetrics(); /** * Communication between the PuppetBidiKeyboard and the actual @@ -1150,7 +1150,7 @@ parent: async BHRThreadHang(HangDetails aHangDetails); - async AddPerformanceMetrics(nsID aID, PerformanceInfo[] aMetrics); + async AddPerformanceMetrics(PerformanceInfo[] aMetrics); both: async AsyncMessage(nsString aMessage, CpowEntry[] aCpows, Principal aPrincipal, ClonedMessageData aData); diff --git a/dom/tests/browser/browser_test_performance_metrics.js b/dom/tests/browser/browser_test_performance_metrics.js index 01a9c81094c3..d38de25f8574 100644 --- a/dom/tests/browser/browser_test_performance_metrics.js +++ b/dom/tests/browser/browser_test_performance_metrics.js @@ -68,8 +68,12 @@ add_task(async function test() { let duration = 0; let total = 0; - function exploreResults(data) { - for (let entry of data) { + function getInfoFromService(subject, topic, value) { + subject = subject.QueryInterface(Ci.nsIMutableArray); + let enumerator = subject.enumerate(); + while (enumerator.hasMoreElements()) { + let entry = enumerator.getNext(); + entry = entry.QueryInterface(Ci.nsIPerformanceMetricsData); if (entry.pid == Services.appinfo.processID) { parent_process_event = true; } @@ -79,8 +83,12 @@ add_task(async function test() { } else { duration += entry.duration; } - // let's look at the data we got back - for (let item of entry.items) { + // let's look at the XPCOM data we got back + let items = entry.items.QueryInterface(Ci.nsIMutableArray); + let enumerator2 = items.enumerate(); + while (enumerator2.hasMoreElements()) { + let item = enumerator2.getNext(); + item = item.QueryInterface(Ci.nsIPerformanceMetricsDispatchCategory); if (entry.worker) { worker_total += item.count; } else { @@ -90,9 +98,17 @@ add_task(async function test() { } } - // get all metrics via the promise - let results = await ChromeUtils.requestPerformanceMetrics(); - exploreResults(results); + Services.obs.addObserver(getInfoFromService, "performance-metrics"); + + // wait until we get some events back by triggering requestPerformanceMetrics + await BrowserTestUtils.waitForCondition(() => { + ChromeUtils.requestPerformanceMetrics(); + return worker_duration > 0 && duration > 0 && parent_process_event; + }, "wait for events to come in", 250, 20); + + BrowserTestUtils.removeTab(page1); + BrowserTestUtils.removeTab(page2); + BrowserTestUtils.removeTab(page3); Assert.ok(worker_duration > 0, "Worker duration should be positive"); Assert.ok(worker_total > 0, "Worker count should be positive"); @@ -101,8 +117,5 @@ add_task(async function test() { Assert.ok(parent_process_event, "parent process sent back some events"); }); - BrowserTestUtils.removeTab(page1); - BrowserTestUtils.removeTab(page2); - BrowserTestUtils.removeTab(page3); SpecialPowers.clearUserPref('dom.performance.enable_scheduler_timing'); }); diff --git a/dom/workers/WorkerDebugger.cpp b/dom/workers/WorkerDebugger.cpp index 81ab25965d8d..cc69715a2b45 100644 --- a/dom/workers/WorkerDebugger.cpp +++ b/dom/workers/WorkerDebugger.cpp @@ -478,7 +478,6 @@ PerformanceInfo WorkerDebugger::ReportPerformanceInfo() { AssertIsOnMainThread(); - #if defined(XP_WIN) uint32_t pid = GetCurrentProcessId(); #else @@ -496,29 +495,22 @@ WorkerDebugger::ReportPerformanceInfo() } } } - + RefPtr perf = mWorkerPrivate->GetPerformanceCounter(); + uint16_t count = perf->GetTotalDispatchCount(); + uint64_t duration = perf->GetExecutionDuration(); + RefPtr uri = mWorkerPrivate->GetResolvedScriptURI(); // Workers only produce metrics for a single category - DispatchCategory::Worker. // We still return an array of CategoryDispatch so the PerformanceInfo // struct is common to all performance counters throughout Firefox. FallibleTArray items; - uint64_t duration = 0; - uint16_t count = 0; - RefPtr uri = mWorkerPrivate->GetResolvedScriptURI(); - - RefPtr perf = mWorkerPrivate->GetPerformanceCounter(); - if (perf) { - count = perf->GetTotalDispatchCount(); - duration = perf->GetExecutionDuration(); - CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count); - if (!items.AppendElement(item, fallible)) { - NS_ERROR("Could not complete the operation"); - return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration, + CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count); + if (!items.AppendElement(item, fallible)) { + NS_ERROR("Could not complete the operation"); + return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration, true, items); - } - perf->ResetPerformanceCounters(); } - + perf->ResetPerformanceCounters(); return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration, true, items); } diff --git a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp deleted file mode 100644 index 8f2d88bb75b0..000000000000 --- a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp +++ /dev/null @@ -1,220 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "nsThreadUtils.h" -#include "mozilla/Logging.h" -#include "mozilla/PerformanceUtils.h" -#include "mozilla/PerformanceMetricsCollector.h" -#include "mozilla/dom/ContentParent.h" -#include "mozilla/dom/Promise.h" -#include "mozilla/dom/WorkerDebugger.h" -#include "mozilla/dom/WorkerDebuggerManager.h" - -using namespace mozilla; -using namespace mozilla::dom; - -static mozilla::LazyLogModule sPerfLog("PerformanceMetricsCollector"); -#ifdef LOG -#undef LOG -#endif -#define LOG(args) MOZ_LOG(sPerfLog, mozilla::LogLevel::Debug, args) - -namespace mozilla { - -// -// class AggregatedResults -// -AggregatedResults::AggregatedResults(nsID aUUID, - PerformanceMetricsCollector* aCollector, - dom::Promise* aPromise) - : mPromise(aPromise) - , mPendingResults(0) - , mCollector(aCollector) - , mUUID(aUUID) -{ - MOZ_ASSERT(aCollector); - MOZ_ASSERT(aPromise); -} - -void -AggregatedResults::Abort(nsresult aReason) -{ - MOZ_ASSERT(mPromise); - MOZ_ASSERT(NS_FAILED(aReason)); - mPromise->MaybeReject(aReason); - mPromise = nullptr; - mPendingResults = 0; -} - -void -AggregatedResults::AppendResult(const nsTArray& aMetrics) -{ - if (!mPromise) { - // A previous call failed and the promise was already rejected - return; - } - MOZ_ASSERT(mPendingResults > 0); - - // Each PerformanceInfo is converted into a PerformanceInfoDictionary - for (const PerformanceInfo& result : aMetrics) { - mozilla::dom::Sequence items; - - for (const CategoryDispatch& entry : result.items()) { - CategoryDispatchDictionary* item = items.AppendElement(fallible); - if (NS_WARN_IF(!item)) { - Abort(NS_ERROR_OUT_OF_MEMORY); - return; - } - item->mCategory = entry.category(); - item->mCount = entry.count(); - } - - PerformanceInfoDictionary* data = mData.AppendElement(fallible); - if (NS_WARN_IF(!data)) { - Abort(NS_ERROR_OUT_OF_MEMORY); - return; - } - data->mPid = result.pid(); - data->mWid = result.wid(); - data->mPwid = result.pwid(); - data->mHost = *result.host().get(); - data->mDuration = result.pid(); - data->mWorker = result.worker(); - data->mItems = items; - } - - mPendingResults--; - if (mPendingResults) { - return; - } - - LOG(("[%s] All data collected, resolving promise", nsIDToCString(mUUID).get())); - mPromise->MaybeResolve(mData); - mCollector->ForgetAggregatedResults(mUUID); -} - -void -AggregatedResults::SetNumResultsRequired(uint32_t aNumResultsRequired) -{ - MOZ_ASSERT(!mPendingResults && aNumResultsRequired); - mPendingResults = aNumResultsRequired; -} - -// -// class PerformanceMetricsCollector (singleton) -// - -// raw pointer for the singleton -PerformanceMetricsCollector* gInstance = nullptr; - -PerformanceMetricsCollector::~PerformanceMetricsCollector() -{ - MOZ_ASSERT(gInstance == this); - gInstance = nullptr; -} - -void -PerformanceMetricsCollector::ForgetAggregatedResults(const nsID& aUUID) -{ - MOZ_ASSERT(gInstance); - MOZ_ASSERT(XRE_IsParentProcess()); - // This Remove() call will trigger AggregatedResults DTOR and if its - // the last in the table, the DTOR of PerformanceMetricsCollector. - // That's why we need to make sure we hold a reference here before the call - RefPtr kungFuDeathGrip = this; - LOG(("[%s] Removing from the table", nsIDToCString(aUUID).get())); - mAggregatedResults.Remove(aUUID); -} - -// static -void -PerformanceMetricsCollector::RequestMetrics(dom::Promise* aPromise) -{ - MOZ_ASSERT(aPromise); - MOZ_ASSERT(XRE_IsParentProcess()); - RefPtr pmc = gInstance; - if (!pmc) { - pmc = new PerformanceMetricsCollector(); - gInstance = pmc; - } - pmc->RequestMetricsInternal(aPromise); -} - -void -PerformanceMetricsCollector::RequestMetricsInternal(dom::Promise* aPromise) -{ - // each request has its own UUID - nsID uuid; - nsresult rv = nsContentUtils::GenerateUUIDInPlace(uuid); - if (NS_WARN_IF(NS_FAILED(rv))) { - aPromise->MaybeReject(rv); - return; - } - - LOG(("[%s] Requesting Performance Metrics", nsIDToCString(uuid).get())); - - // Getting all content processes - nsTArray children; - ContentParent::GetAll(children); - - uint32_t numChildren = children.Length(); - LOG(("[%s] Expecting %d results back", nsIDToCString(uuid).get(), numChildren + 1)); - - // keep track of all results in an AggregatedResults instance - UniquePtr results = MakeUnique(uuid, this, aPromise); - - // We want to get back as many results as children, plus the result - // from the content parent itself - results->SetNumResultsRequired(numChildren + 1); - mAggregatedResults.Put(uuid, std::move(results)); - - // calling all content processes via IPDL (async) - for (uint32_t i = 0; i < numChildren; i++) { - if (NS_WARN_IF(!children[i]->SendRequestPerformanceMetrics(uuid))) { - LOG(("[%s] Failed to send request to child %d", nsIDToCString(uuid).get(), i)); - mAggregatedResults.GetValue(uuid)->get()->Abort(NS_ERROR_FAILURE); - return; - } - } - - // collecting the current process PerformanceInfo - nsTArray info; - CollectPerformanceInfo(info); - DataReceived(uuid, info); -} - - -// static -nsresult -PerformanceMetricsCollector::DataReceived(const nsID& aUUID, - const nsTArray& aMetrics) -{ - MOZ_ASSERT(gInstance); - MOZ_ASSERT(XRE_IsParentProcess()); - return gInstance->DataReceivedInternal(aUUID, aMetrics); -} - -nsresult -PerformanceMetricsCollector::DataReceivedInternal(const nsID& aUUID, - const nsTArray& aMetrics) -{ - MOZ_ASSERT(gInstance == this); - UniquePtr* results = mAggregatedResults.GetValue(aUUID); - if (!results) { - return NS_ERROR_FAILURE; - } - - LOG(("[%s] Received one PerformanceInfo array", nsIDToCString(aUUID).get())); - AggregatedResults* aggregatedResults = results->get(); - MOZ_ASSERT(aggregatedResults); - - // If this is the last result, APpendResult() will trigger the deletion - // of this collector, nothing should be done after this line. - aggregatedResults->AppendResult(aMetrics); - return NS_OK; -} - -} // namespace diff --git a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h deleted file mode 100644 index 926157cffc4b..000000000000 --- a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h +++ /dev/null @@ -1,87 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; 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 - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef PerformanceMetricsCollector_h -#define PerformanceMetricsCollector_h - -#include "nsID.h" -#include "mozilla/dom/ChromeUtilsBinding.h" // defines PerformanceInfoDictionary -#include "mozilla/dom/DOMTypes.h" // defines PerformanceInfo - -namespace mozilla { - -namespace dom { - class Promise; -} - -class PerformanceMetricsCollector; - -// AggregatedResults receives PerformanceInfo results that are collected -// via IPDL from all content processes and the main process. They -// are converted into an array of PerformanceInfoDictionary dictionaries (webidl) -// -// The class is instanciated with a Promise and a number of processes -// that are supposed to send back results. -// -// Once every process have sent back its results, AggregatedResults will -// resolve the promise with all the collected data and send back the -// dictionnary. -// -class AggregatedResults final -{ -public: - AggregatedResults(nsID aUUID, PerformanceMetricsCollector* aCollector, - dom::Promise* aPromise); - ~AggregatedResults() = default; - void AppendResult(const nsTArray& aMetrics); - void SetNumResultsRequired(uint32_t aNumResultsRequired); - void Abort(nsresult aReason); - -private: - RefPtr mPromise; - uint32_t mPendingResults; - FallibleTArray mData; - - // AggregatedResults keeps a reference on the collector - // so it gets destructed when all pending AggregatedResults - // are themselves destructed when removed from - // PerformanceMetricsCollector::mAggregatedResults. - // - // This lifecycle ensures that everything is released once - // all pending results are sent. - RefPtr mCollector; - nsID mUUID; -}; - -// -// PerformanceMetricsCollector is instanciated as a singleton, and creates -// one AggregatedResults instance everytime metrics are requested. -// -// Each AggregatedResults has a unique identifier (UUID) that is used -// to send metrics requests via IPDL. When metrics are back in an -// asynchronous fashion, the UUID is used to append the data to the -// right AggregatedResults instance and eventually let it resolve the -// linked promise. -// -class PerformanceMetricsCollector final -{ -public: - NS_INLINE_DECL_REFCOUNTING(PerformanceMetricsCollector) - - static void RequestMetrics(dom::Promise* aPromise); - static nsresult DataReceived(const nsID& aUUID, - const nsTArray& aMetrics); - void ForgetAggregatedResults(const nsID& aUUID); - -private: - ~PerformanceMetricsCollector(); - void RequestMetricsInternal(dom::Promise* aPromise); - nsresult DataReceivedInternal(const nsID& aUUID, - const nsTArray& aMetrics); - nsDataHashtable> mAggregatedResults; -}; - -} // namespace mozilla -#endif // PerformanceMetricsCollector_h diff --git a/toolkit/components/perfmonitoring/PerformanceUtils.cpp b/toolkit/components/perfmonitoring/PerformanceUtils.cpp index f094db111d5a..3643839dbb21 100644 --- a/toolkit/components/perfmonitoring/PerformanceUtils.cpp +++ b/toolkit/components/perfmonitoring/PerformanceUtils.cpp @@ -4,6 +4,8 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +#include "nsIMutableArray.h" +#include "nsPerformanceMetrics.h" #include "nsThreadUtils.h" #include "mozilla/PerformanceUtils.h" #include "mozilla/dom/DocGroup.h" @@ -39,4 +41,50 @@ CollectPerformanceInfo(nsTArray& aMetrics) } } +nsresult +NotifyPerformanceInfo(const nsTArray& aMetrics) +{ + nsresult rv; + + nsCOMPtr array = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (NS_WARN_IF(!array)) { + return NS_ERROR_FAILURE; + } + + // Each PerformanceInfo is converted into a nsIPerformanceMetricsData + for (const PerformanceInfo& info : aMetrics) { + nsCOMPtr items = do_CreateInstance(NS_ARRAY_CONTRACTID); + if (NS_WARN_IF(!items)) { + return rv; + } + for (const CategoryDispatch& entry : info.items()) { + nsCOMPtr item = + new PerformanceMetricsDispatchCategory(entry.category(), + entry.count()); + rv = items->AppendElement(item); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + nsCOMPtr data; + data = new PerformanceMetricsData(info.pid(), info.wid(), info.pwid(), + info.host(), info.duration(), + info.worker(), items); + rv = array->AppendElement(data); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + } + nsCOMPtr obs = mozilla::services::GetObserverService(); + if (NS_WARN_IF(!obs)) { + return NS_ERROR_FAILURE; + } + + rv = obs->NotifyObservers(array, "performance-metrics", nullptr); + if (NS_WARN_IF(NS_FAILED(rv))) { + return rv; + } + return NS_OK; +} + } // namespace diff --git a/toolkit/components/perfmonitoring/PerformanceUtils.h b/toolkit/components/perfmonitoring/PerformanceUtils.h index 42505f1adf6f..71a4d3e478ab 100644 --- a/toolkit/components/perfmonitoring/PerformanceUtils.h +++ b/toolkit/components/perfmonitoring/PerformanceUtils.h @@ -3,8 +3,8 @@ * 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 PerformanceUtils_h -#define PerformanceUtils_h +#ifndef PerformanceCollector_h +#define PerformanceCollector_h #include "mozilla/dom/DOMTypes.h" // defines PerformanceInfo @@ -12,9 +12,15 @@ namespace mozilla { /** * Collects all performance info in the current process - * and adds then in the aMetrics array + * and adds then in the aMetrics arrey */ void CollectPerformanceInfo(nsTArray& aMetrics); +/** + * Converts a PerformanceInfo array into a nsIPerformanceMetricsData and + * sends a performance-metrics notification with it + */ +nsresult NotifyPerformanceInfo(const nsTArray& aMetrics); + } // namespace mozilla -#endif // PerformanceUtils_h +#endif // PerformanceCollector_h diff --git a/toolkit/components/perfmonitoring/moz.build b/toolkit/components/perfmonitoring/moz.build index 3423c92a040e..ebc3bd8d192d 100644 --- a/toolkit/components/perfmonitoring/moz.build +++ b/toolkit/components/perfmonitoring/moz.build @@ -27,12 +27,10 @@ UNIFIED_SOURCES += [ ] UNIFIED_SOURCES += [ - 'PerformanceMetricsCollector.cpp', 'PerformanceUtils.cpp' ] EXPORTS.mozilla += [ - 'PerformanceMetricsCollector.h', 'PerformanceUtils.h' ]