diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /toolkit/components/perfmonitoring | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esr
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'toolkit/components/perfmonitoring')
6 files changed, 693 insertions, 0 deletions
diff --git a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp new file mode 100644 index 0000000000..9a02c39bc5 --- /dev/null +++ b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.cpp @@ -0,0 +1,315 @@ +/* -*- 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/AbstractThread.h" +#include "mozilla/Logging.h" +#include "mozilla/PerformanceUtils.h" +#include "mozilla/PerformanceMetricsCollector.h" +#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/TaskQueue.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 IPCTimeout +// +NS_IMPL_ISUPPORTS(IPCTimeout, nsITimerCallback, nsINamed) + +// static +IPCTimeout* IPCTimeout::CreateInstance(AggregatedResults* aResults) { + MOZ_ASSERT(aResults); + uint32_t delay = StaticPrefs::dom_performance_children_results_ipc_timeout(); + if (delay == 0) { + return nullptr; + } + return new IPCTimeout(aResults, delay); +} + +IPCTimeout::IPCTimeout(AggregatedResults* aResults, uint32_t aDelay) + : mResults(aResults) { + MOZ_ASSERT(aResults); + MOZ_ASSERT(aDelay > 0); + mozilla::DebugOnly<nsresult> rv = NS_NewTimerWithCallback( + getter_AddRefs(mTimer), this, aDelay, nsITimer::TYPE_ONE_SHOT); + MOZ_ASSERT(NS_SUCCEEDED(rv)); + LOG(("IPCTimeout timer created")); +} + +IPCTimeout::~IPCTimeout() { Cancel(); } + +void IPCTimeout::Cancel() { + if (mTimer) { + LOG(("IPCTimeout timer canceled")); + mTimer->Cancel(); + mTimer = nullptr; + } +} + +NS_IMETHODIMP +IPCTimeout::Notify(nsITimer* aTimer) { + LOG(("IPCTimeout timer triggered")); + mResults->ResolveNow(); + return NS_OK; +} + +NS_IMETHODIMP +IPCTimeout::GetName(nsACString& aName) { + aName.AssignLiteral("IPCTimeout"); + return NS_OK; +} + +// +// class AggregatedResults +// +AggregatedResults::AggregatedResults(nsID aUUID, + PerformanceMetricsCollector* aCollector) + : mPendingResults(0), mCollector(aCollector), mUUID(aUUID) { + MOZ_ASSERT(aCollector); + mIPCTimeout = IPCTimeout::CreateInstance(this); +} + +void AggregatedResults::Abort(nsresult aReason) { + MOZ_ASSERT(!mHolder.IsEmpty()); + MOZ_ASSERT(NS_FAILED(aReason)); + if (mIPCTimeout) { + mIPCTimeout->Cancel(); + mIPCTimeout = nullptr; + } + mHolder.Reject(aReason, __func__); + mPendingResults = 0; +} + +void AggregatedResults::ResolveNow() { + MOZ_ASSERT(!mHolder.IsEmpty()); + LOG(("[%s] Early resolve", nsIDToCString(mUUID).get())); + mHolder.Resolve(CopyableTArray(mData), __func__); + mIPCTimeout = nullptr; + mCollector->ForgetAggregatedResults(mUUID); +} + +void AggregatedResults::AppendResult( + const nsTArray<dom::PerformanceInfo>& aMetrics) { + if (mHolder.IsEmpty()) { + // 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<mozilla::dom::CategoryDispatchDictionary> items; + + for (const CategoryDispatch& entry : result.items()) { + uint32_t count = entry.count(); + if (count == 0) { + continue; + } + CategoryDispatchDictionary* item = items.AppendElement(fallible); + if (NS_WARN_IF(!item)) { + Abort(NS_ERROR_OUT_OF_MEMORY); + return; + } + item->mCategory = entry.category(); + item->mCount = count; + } + + PerformanceInfoDictionary* data = mData.AppendElement(fallible); + if (NS_WARN_IF(!data)) { + Abort(NS_ERROR_OUT_OF_MEMORY); + return; + } + data->mPid = result.pid(); + data->mWindowId = result.windowId(); + data->mHost.Assign(result.host()); + data->mDuration = result.duration(); + data->mCounterId = result.counterId(); + data->mIsWorker = result.isWorker(); + data->mIsTopLevel = result.isTopLevel(); + data->mMemoryInfo.mDomDom = result.memory().domDom(); + data->mMemoryInfo.mDomStyle = result.memory().domStyle(); + data->mMemoryInfo.mDomOther = result.memory().domOther(); + data->mMemoryInfo.mJsMemUsage = result.memory().jsMemUsage(); + data->mMemoryInfo.mMedia.mAudioSize = result.memory().media().audioSize(); + data->mMemoryInfo.mMedia.mVideoSize = result.memory().media().videoSize(); + data->mMemoryInfo.mMedia.mResourcesSize = + result.memory().media().resourcesSize(); + data->mItems = items; + } + + mPendingResults--; + if (mPendingResults) { + return; + } + + LOG(("[%s] All data collected, resolving promise", + nsIDToCString(mUUID).get())); + if (mIPCTimeout) { + mIPCTimeout->Cancel(); + mIPCTimeout = nullptr; + } + nsTArray<dom::PerformanceInfoDictionary> data; + data.Assign(mData); + mHolder.Resolve(std::move(data), __func__); + mCollector->ForgetAggregatedResults(mUUID); +} + +void AggregatedResults::SetNumResultsRequired(uint32_t aNumResultsRequired) { + MOZ_ASSERT(!mPendingResults && aNumResultsRequired); + mPendingResults = aNumResultsRequired; +} + +RefPtr<RequestMetricsPromise> AggregatedResults::GetPromise() { + return mHolder.Ensure(__func__); +} + +// +// 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<PerformanceMetricsCollector> kungFuDeathGrip = this; + LOG(("[%s] Removing from the table", nsIDToCString(aUUID).get())); + mAggregatedResults.Remove(aUUID); +} + +// static +RefPtr<RequestMetricsPromise> PerformanceMetricsCollector::RequestMetrics() { + MOZ_ASSERT(XRE_IsParentProcess()); + RefPtr<PerformanceMetricsCollector> pmc = gInstance; + if (!pmc) { + pmc = new PerformanceMetricsCollector(); + gInstance = pmc; + } + return pmc->RequestMetricsInternal(); +} + +RefPtr<RequestMetricsPromise> +PerformanceMetricsCollector::RequestMetricsInternal() { + // each request has its own UUID + nsID uuid; + nsresult rv = nsID::GenerateUUIDInPlace(uuid); + if (NS_WARN_IF(NS_FAILED(rv))) { + return RequestMetricsPromise::CreateAndReject(rv, __func__); + } + + LOG(("[%s] Requesting Performance Metrics", nsIDToCString(uuid).get())); + + // Getting all content processes + nsTArray<ContentParent*> children; + ContentParent::GetAll(children); + uint32_t numChildren = children.Length(); + + // keep track of all results in an AggregatedResults instance + UniquePtr<AggregatedResults> results = + MakeUnique<AggregatedResults>(uuid, this); + RefPtr<RequestMetricsPromise> promise = results->GetPromise(); + + // We want to get back as many results as children + one parent if needed + uint32_t numResultsRequired = children.Length(); + nsTArray<RefPtr<PerformanceInfoPromise>> localPromises = + CollectPerformanceInfo(); + if (!localPromises.IsEmpty()) { + numResultsRequired++; + } + + LOG(("[%s] Expecting %d results back", nsIDToCString(uuid).get(), + numResultsRequired)); + results->SetNumResultsRequired(numResultsRequired); + const auto& aggregatedResult = + mAggregatedResults.InsertOrUpdate(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)); + aggregatedResult->Abort(NS_ERROR_FAILURE); + return RequestMetricsPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + LOG(("[%s] Request sent to child %d", nsIDToCString(uuid).get(), i)); + } + + nsTArray<RefPtr<PerformanceInfoPromise>> promises = CollectPerformanceInfo(); + if (promises.IsEmpty()) { + return promise; + } + + // collecting the current process PerformanceInfo + PerformanceInfoPromise::All(NS_GetCurrentThread(), localPromises) + ->Then( + NS_GetCurrentThread(), __func__, + [uuid](const nsTArray<mozilla::dom::PerformanceInfo> aResult) { + LOG(("[%s] Local CollectPerformanceInfo promise resolved", + nsIDToCString(uuid).get())); + DataReceived(uuid, aResult); + }, + [](const nsresult aResult) {}); + + return promise; +} + +// static +nsresult PerformanceMetricsCollector::DataReceived( + const nsID& aUUID, const nsTArray<PerformanceInfo>& aMetrics) { + // If some content process were unresponsive on shutdown, we may get called + // here with late data received from children - so instead of asserting + // that gInstance is available, we just return. + if (!gInstance) { + LOG(("[%s] gInstance is gone", nsIDToCString(aUUID).get())); + return NS_OK; + } + MOZ_ASSERT(XRE_IsParentProcess()); + return gInstance->DataReceivedInternal(aUUID, aMetrics); +} + +nsresult PerformanceMetricsCollector::DataReceivedInternal( + const nsID& aUUID, const nsTArray<PerformanceInfo>& aMetrics) { + MOZ_ASSERT(gInstance == this); + auto results = mAggregatedResults.Lookup(aUUID); + if (!results) { + LOG(("[%s] UUID is gone from mAggregatedResults", + nsIDToCString(aUUID).get())); + 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 mozilla diff --git a/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h new file mode 100644 index 0000000000..1b9e90f3b4 --- /dev/null +++ b/toolkit/components/perfmonitoring/PerformanceMetricsCollector.h @@ -0,0 +1,106 @@ +/* -*- 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 "nsIObserver.h" +#include "nsITimer.h" +#include "nsID.h" +#include "nsTHashMap.h" +#include "mozilla/dom/ChromeUtilsBinding.h" // defines PerformanceInfoDictionary +#include "mozilla/dom/DOMTypes.h" // defines PerformanceInfo +#include "mozilla/PerformanceTypes.h" + +namespace mozilla { + +namespace dom { +class Promise; +} + +class PerformanceMetricsCollector; +class AggregatedResults; + +class IPCTimeout final : public nsITimerCallback, public nsINamed { + public: + NS_DECL_NSITIMERCALLBACK + NS_DECL_NSINAMED + NS_DECL_ISUPPORTS + static IPCTimeout* CreateInstance(AggregatedResults* aResults); + void Cancel(); + + private: + IPCTimeout(AggregatedResults* aResults, uint32_t aDelay); + ~IPCTimeout(); + + nsCOMPtr<nsITimer> mTimer; + AggregatedResults* mResults; +}; + +// AggregatedResults receives PerformanceInfo results that are collected +// asynchronously via IPDL from all content processes. +// They are converted into an array of +// PerformanceInfoDictionary dictionaries (webidl) +// +// Once every process have sent back its results, AggregatedResults will +// resolve the MozPromise returned by GetPromise() +// with all the collected data. +// +// See ChromeUtils::RequestPerformanceMetrics. +class AggregatedResults final { + public: + AggregatedResults(nsID aUUID, PerformanceMetricsCollector* aCollector); + ~AggregatedResults() = default; + void AppendResult(const nsTArray<dom::PerformanceInfo>& aMetrics); + void SetNumResultsRequired(uint32_t aNumResultsRequired); + void Abort(nsresult aReason); + void ResolveNow(); + RefPtr<RequestMetricsPromise> GetPromise(); + + private: + RefPtr<IPCTimeout> mIPCTimeout; + MozPromiseHolder<RequestMetricsPromise> mHolder; + uint32_t mPendingResults; + FallibleTArray<dom::PerformanceInfoDictionary> 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<PerformanceMetricsCollector> 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 RefPtr<RequestMetricsPromise> RequestMetrics(); + static nsresult DataReceived(const nsID& aUUID, + const nsTArray<dom::PerformanceInfo>& aMetrics); + void ForgetAggregatedResults(const nsID& aUUID); + + private: + ~PerformanceMetricsCollector(); + RefPtr<RequestMetricsPromise> RequestMetricsInternal(); + nsresult DataReceivedInternal(const nsID& aUUID, + const nsTArray<dom::PerformanceInfo>& aMetrics); + nsTHashMap<nsID, UniquePtr<AggregatedResults>> mAggregatedResults; +}; + +} // namespace mozilla +#endif // PerformanceMetricsCollector_h diff --git a/toolkit/components/perfmonitoring/PerformanceTypes.h b/toolkit/components/perfmonitoring/PerformanceTypes.h new file mode 100644 index 0000000000..9ef53c1d13 --- /dev/null +++ b/toolkit/components/perfmonitoring/PerformanceTypes.h @@ -0,0 +1,31 @@ +/* -*- 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 PerformanceTypes_h +#define PerformanceTypes_h + +#include "mozilla/MozPromise.h" + +namespace mozilla { + +namespace dom { +class PerformanceInfo; +class PerformanceMemoryInfo; +struct PerformanceInfoDictionary; +} // namespace dom + +/** + * Promises definitions + */ +typedef MozPromise<dom::PerformanceInfo, nsresult, true> PerformanceInfoPromise; +typedef MozPromise<nsTArray<dom::PerformanceInfoDictionary>, nsresult, true> + RequestMetricsPromise; +typedef MozPromise<nsTArray<dom::PerformanceInfo>, nsresult, true> + PerformanceInfoArrayPromise; +typedef MozPromise<mozilla::dom::PerformanceMemoryInfo, nsresult, true> + MemoryPromise; + +} // namespace mozilla +#endif // PerformanceTypes_h diff --git a/toolkit/components/perfmonitoring/PerformanceUtils.cpp b/toolkit/components/perfmonitoring/PerformanceUtils.cpp new file mode 100644 index 0000000000..08ee695854 --- /dev/null +++ b/toolkit/components/perfmonitoring/PerformanceUtils.cpp @@ -0,0 +1,186 @@ +/* -*- 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 "mozilla/PerformanceUtils.h" + +#include "mozilla/dom/DOMTypes.h" +#include "mozilla/dom/DocGroup.h" +#include "mozilla/dom/BrowsingContextGroup.h" +#include "mozilla/dom/Document.h" +#include "mozilla/dom/WorkerDebugger.h" +#include "mozilla/dom/WorkerDebuggerManager.h" + +#include "MediaDecoder.h" +#include "jsfriendapi.h" +#include "nsGlobalWindowOuter.h" +#include "nsWindowSizes.h" + +using namespace mozilla; +using namespace mozilla::dom; + +namespace mozilla { + +nsTArray<RefPtr<PerformanceInfoPromise>> CollectPerformanceInfo() { + nsTArray<RefPtr<PerformanceInfoPromise>> promises; + + // collecting ReportPerformanceInfo from all WorkerDebugger instances + RefPtr<mozilla::dom::WorkerDebuggerManager> wdm = + WorkerDebuggerManager::GetOrCreate(); + if (NS_WARN_IF(!wdm)) { + return promises; + } + + for (uint32_t i = 0; i < wdm->GetDebuggersLength(); i++) { + const RefPtr<WorkerDebugger> debugger = wdm->GetDebuggerAt(i); + promises.AppendElement(debugger->ReportPerformanceInfo()); + } + + nsTArray<RefPtr<BrowsingContextGroup>> groups; + BrowsingContextGroup::GetAllGroups(groups); + + nsTArray<DocGroup*> docGroups; + for (auto& browsingContextGroup : groups) { + browsingContextGroup->GetDocGroups(docGroups); + } + + for (DocGroup* docGroup : docGroups) { + promises.AppendElement(docGroup->ReportPerformanceInfo()); + } + + return promises; +} + +static void AddWindowTabSizes(nsGlobalWindowOuter* aWindow, + nsTabSizes* aSizes) { + Document* document = aWindow->GetDocument(); + if (document && document->GetCachedSizes(aSizes)) { + // We got a cached version + return; + } + // We measure the sizes on a fresh nsTabSizes instance + // because we want to cache the value and aSizes might + // already have some values from other windows. + nsTabSizes sizes; + + // Measure the window. + SizeOfState state(moz_malloc_size_of); + nsWindowSizes windowSizes(state); + aWindow->AddSizeOfIncludingThis(windowSizes); + // Measure the inner window, if there is one. + nsGlobalWindowInner* inner = aWindow->GetCurrentInnerWindowInternal(); + if (inner != nullptr) { + inner->AddSizeOfIncludingThis(windowSizes); + } + windowSizes.addToTabSizes(&sizes); + if (document) { + document->SetCachedSizes(&sizes); + } + aSizes->mDom += sizes.mDom; + aSizes->mStyle += sizes.mStyle; + aSizes->mOther += sizes.mOther; +} + +RefPtr<MemoryPromise> CollectMemoryInfo( + const RefPtr<DocGroup>& aDocGroup, + const RefPtr<AbstractThread>& aEventTarget) { + // Getting Dom sizes. + nsTabSizes sizes; + + using WindowSet = mozilla::HashSet<nsGlobalWindowOuter*>; + WindowSet windowsVisited; + for (const auto& document : *aDocGroup) { + nsGlobalWindowOuter* window = + document ? nsGlobalWindowOuter::Cast(document->GetWindow()) : nullptr; + if (window) { + WindowSet::AddPtr p = windowsVisited.lookupForAdd(window); + if (!p) { + // We have not seen this window before. + AddWindowTabSizes(window, &sizes); + if (!windowsVisited.add(p, window)) { + // OOM. Let us stop counting memory, we may undercount. + break; + } + } + } + } + + using ZoneSet = mozilla::HashSet<JS::Zone*>; + using SharedSet = mozilla::HashSet<void*>; + ZoneSet zonesVisited; + SharedSet sharedVisited; + // Getting JS-related memory usage + uint64_t jsMemUsed = 0; + nsTArray<RefPtr<MediaMemoryPromise>> mediaMemoryPromises; + for (auto* doc : *aDocGroup) { + bool unused; + nsIGlobalObject* globalObject = doc->GetScriptHandlingObject(unused); + if (globalObject) { + JSObject* object = globalObject->GetGlobalJSObject(); + if (object != nullptr) { + MOZ_ASSERT(NS_IsMainThread(), + "We cannot get the object zone on another thread"); + JS::Zone* zone = JS::GetObjectZone(object); + ZoneSet::AddPtr addZone = zonesVisited.lookupForAdd(zone); + if (!addZone) { + // We have not checked this zone before. + jsMemUsed += js::GetMemoryUsageForZone(zone); + if (!zonesVisited.add(addZone, zone)) { + // OOM. Let us stop counting memory, we may undercount. + break; + } + + const js::gc::SharedMemoryMap& shared = + js::GetSharedMemoryUsageForZone(zone); + for (auto iter = shared.iter(); !iter.done(); iter.next()) { + void* sharedMem = iter.get().key(); + SharedSet::AddPtr addShared = sharedVisited.lookupForAdd(sharedMem); + if (addShared) { + // We *have* seen this shared memory before. + + // Because shared memory is already included in + // js::GetMemoryUsageForZone() above, and we've seen it for a + // previous zone, we subtract it here so it's not counted more + // than once. + jsMemUsed -= iter.get().value().nbytes; + } else if (!sharedVisited.add(addShared, sharedMem)) { + // As above, abort with an under-estimate. + break; + } + } + } + } + } + + mediaMemoryPromises.AppendElement(GetMediaMemorySizes(doc)); + } + + // Getting Media sizes. + return MediaMemoryPromise::All(aEventTarget, mediaMemoryPromises) + ->Then( + aEventTarget, __func__, + [jsMemUsed, sizes](const nsTArray<MediaMemoryInfo> mediaArray) { + size_t audioSize = 0; + size_t videoSize = 0; + size_t resourcesSize = 0; + + for (auto media : mediaArray) { + audioSize += media.audioSize(); + videoSize += media.videoSize(); + resourcesSize += media.resourcesSize(); + } + + return MemoryPromise::CreateAndResolve( + PerformanceMemoryInfo( + MediaMemoryInfo(audioSize, videoSize, resourcesSize), + sizes.mDom, sizes.mStyle, sizes.mOther, jsMemUsed), + __func__); + }, + [](const nsresult rv) { + return MemoryPromise::CreateAndReject(rv, __func__); + }); +} + +} // namespace mozilla diff --git a/toolkit/components/perfmonitoring/PerformanceUtils.h b/toolkit/components/perfmonitoring/PerformanceUtils.h new file mode 100644 index 0000000000..7c01795f28 --- /dev/null +++ b/toolkit/components/perfmonitoring/PerformanceUtils.h @@ -0,0 +1,32 @@ +/* -*- 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 PerformanceUtils_h +#define PerformanceUtils_h + +#include "mozilla/PerformanceTypes.h" + +class nsPIDOMWindowOuter; + +namespace mozilla { +namespace dom { +class BrowsingContext; +class DocGroup; +} // namespace dom +/** + * Returns an array of promises to asynchronously collect all performance + * info in the current process. + */ +nsTArray<RefPtr<PerformanceInfoPromise>> CollectPerformanceInfo(); + +/** + * Asynchronously collects memory info for a given window + */ +RefPtr<MemoryPromise> CollectMemoryInfo( + const RefPtr<dom::DocGroup>& aDocGroup, + const RefPtr<AbstractThread>& aEventTarget); + +} // namespace mozilla +#endif // PerformanceUtils_h diff --git a/toolkit/components/perfmonitoring/moz.build b/toolkit/components/perfmonitoring/moz.build new file mode 100644 index 0000000000..193e5df7db --- /dev/null +++ b/toolkit/components/perfmonitoring/moz.build @@ -0,0 +1,23 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +with Files("**"): + BUG_COMPONENT = ("Toolkit", "Performance Monitoring") + + +UNIFIED_SOURCES += ["PerformanceMetricsCollector.cpp", "PerformanceUtils.cpp"] + +EXPORTS.mozilla += [ + "PerformanceMetricsCollector.h", + "PerformanceTypes.h", + "PerformanceUtils.h", +] + +LOCAL_INCLUDES += ["/dom/base"] + +FINAL_LIBRARY = "xul" + +include("/ipc/chromium/chromium-config.mozbuild") |