--- a/dom/base/ChromeUtils.cpp
+++ b/dom/base/ChromeUtils.cpp
@@ -8,16 +8,17 @@
#include "jsfriendapi.h"
#include "WrapperFactory.h"
#include "mozilla/Base64.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/TimeStamp.h"
+#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/IdleDeadline.h"
#include "mozilla/dom/UnionTypes.h"
#include "mozilla/dom/WindowBinding.h" // For IdleRequestCallback/Options
#include "nsThreadUtils.h"
#include "mozJSComponentLoader.h"
#include "GeckoProfiler.h"
namespace mozilla {
@@ -648,16 +649,29 @@ ChromeUtils::ClearRecentJSDevError(Globa
{
auto runtime = CycleCollectedJSRuntime::Get();
MOZ_ASSERT(runtime);
runtime->ClearRecentDevError();
}
#endif // NIGHTLY_BUILD
+#ifndef RELEASE_OR_BETA
+/* static */ void
+ChromeUtils::RequestPerformanceMetrics(GlobalObject&)
+{
+ MOZ_ASSERT(XRE_IsParentProcess());
+ nsTArray<ContentParent*> children;
+ ContentParent::GetAll(children);
+ for (uint32_t i = 0; i < children.Length(); i++) {
+ mozilla::Unused << children[i]->SendRequestPerformanceMetrics();
+ }
+}
+#endif
+
constexpr auto kSkipSelfHosted = JS::SavedFrameSelfHosted::Exclude;
/* static */ void
ChromeUtils::GetCallerLocation(const GlobalObject& aGlobal, nsIPrincipal* aPrincipal,
JS::MutableHandle<JSObject*> aRetval)
{
JSContext* cx = aGlobal.Context();
--- a/dom/base/ChromeUtils.h
+++ b/dom/base/ChromeUtils.h
@@ -150,16 +150,20 @@ public:
ErrorResult& aRv);
static void GetRecentJSDevError(GlobalObject& aGlobal,
JS::MutableHandleValue aRetval,
ErrorResult& aRv);
static void ClearRecentJSDevError(GlobalObject& aGlobal);
+#ifndef RELEASE_OR_BETA
+ static void RequestPerformanceMetrics(GlobalObject& aGlobal);
+#endif
+
static void Import(const GlobalObject& aGlobal,
const nsAString& aResourceURI,
const Optional<JS::Handle<JSObject*>>& aTargetObj,
JS::MutableHandle<JSObject*> aRetval,
ErrorResult& aRv);
static void DefineModuleGetter(const GlobalObject& global,
JS::Handle<JSObject*> target,
--- a/dom/base/DocGroup.cpp
+++ b/dom/base/DocGroup.cpp
@@ -1,19 +1,25 @@
/* -*- 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/dom/DocGroup.h"
+#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/TabGroup.h"
#include "mozilla/Telemetry.h"
#include "nsIDocShell.h"
#include "nsDOMMutationObserver.h"
+#if defined(XP_WIN)
+#include <processthreadsapi.h> // for GetCurrentProcessId()
+#else
+#include <unistd.h> // for getpid()
+#endif // defined(XP_WIN)
namespace mozilla {
namespace dom {
AutoTArray<RefPtr<DocGroup>, 2>* DocGroup::sPendingDocGroups = nullptr;
/* static */ nsresult
DocGroup::GetKey(nsIPrincipal* aPrincipal, nsACString& aKey)
@@ -56,16 +62,78 @@ DocGroup::~DocGroup()
if (!NS_IsMainThread()) {
nsIEventTarget* target = EventTargetFor(TaskCategory::Other);
NS_ProxyRelease("DocGroup::mReactionsStack", target, mReactionsStack.forget());
}
mTabGroup->mDocGroups.RemoveEntry(mKey);
}
+#ifndef RELEASE_OR_BETA
+PerformanceInfo
+DocGroup::ReportPerformanceInfo()
+{
+ AssertIsOnMainThread();
+#if defined(XP_WIN)
+ uint32_t pid = GetCurrentProcessId();
+#else
+ uint32_t pid = getpid();
+#endif
+ uint64_t wid = 0;
+ uint64_t pwid = 0;
+ uint16_t count = 0;
+ uint64_t duration = 0;
+ nsCString host = NS_LITERAL_CSTRING("None");
+
+ for (const auto& document : *this) {
+ // grabbing the host name of the first document
+ nsCOMPtr<nsIDocument> doc = do_QueryInterface(document);
+ MOZ_ASSERT(doc);
+ nsCOMPtr<nsIURI> docURI = doc->GetDocumentURI();
+ if (!docURI) {
+ continue;
+ }
+ docURI->GetHost(host);
+ wid = doc->OuterWindowID();
+
+ // getting the top window id - if not possible
+ // pwid gets the same value than wid
+ pwid = wid;
+ nsPIDOMWindowInner* win = doc->GetInnerWindow();
+ if (win) {
+ nsPIDOMWindowOuter* outer = win->GetOuterWindow();
+ if (outer) {
+ nsCOMPtr<nsPIDOMWindowOuter> top = outer->GetTop();
+ if (top) {
+ pwid = top->WindowID();
+ }
+ }
+ }
+ }
+
+ duration = mPerformanceCounter->GetExecutionDuration();
+ FallibleTArray<CategoryDispatch> items;
+
+ // now that we have the host and window ids, let's look at the perf counters
+ for (uint32_t index = 0; index < (uint32_t)TaskCategory::Count; index++) {
+ TaskCategory category = static_cast<TaskCategory>(index);
+ count = mPerformanceCounter->GetDispatchCount(DispatchCategory(category));
+ CategoryDispatch item = CategoryDispatch(index, count);
+ if (!items.AppendElement(item, fallible)) {
+ NS_ERROR("Could not complete the operation");
+ return PerformanceInfo(host, pid, wid, pwid, duration, false, items);
+ }
+ }
+
+ // setting back all counters to zero
+ mPerformanceCounter->ResetPerformanceCounters();
+ return PerformanceInfo(host, pid, wid, pwid, duration, false, items);
+}
+#endif
+
nsresult
DocGroup::Dispatch(TaskCategory aCategory,
already_AddRefed<nsIRunnable>&& aRunnable)
{
#ifndef RELEASE_OR_BETA
mPerformanceCounter->IncrementDispatchCounter(DispatchCategory(aCategory));
#endif
return mTabGroup->DispatchWithDocGroup(aCategory, Move(aRunnable), this);
--- a/dom/base/DocGroup.h
+++ b/dom/base/DocGroup.h
@@ -18,16 +18,18 @@
#include "mozilla/dom/HTMLSlotElement.h"
#include "mozilla/PerformanceCounter.h"
namespace mozilla {
class AbstractThread;
namespace dom {
+class PerformanceInfo;
+
// Two browsing contexts are considered "related" if they are reachable from one
// another through window.opener, window.parent, or window.frames. This is the
// spec concept of a "unit of related browsing contexts"
//
// Two browsing contexts are considered "similar-origin" if they can be made to
// have the same origin by setting document.domain. This is the spec concept of
// a "unit of similar-origin related browsing contexts"
//
@@ -56,16 +58,19 @@ public:
{
return aKey == mKey;
}
#ifndef RELEASE_OR_BETA
PerformanceCounter* GetPerformanceCounter()
{
return mPerformanceCounter;
}
+
+ PerformanceInfo
+ ReportPerformanceInfo();
#endif
TabGroup* GetTabGroup()
{
return mTabGroup;
}
mozilla::dom::CustomElementReactionsStack* CustomElementReactionsStack()
{
MOZ_ASSERT(NS_IsMainThread());
--- a/dom/base/moz.build
+++ b/dom/base/moz.build
@@ -21,16 +21,17 @@ XPIDL_SOURCES += [
'nsIDOMDOMRequest.idl',
'nsIDOMParser.idl',
'nsIDOMSerializer.idl',
'nsIDroppedLinkHandler.idl',
'nsIFrameLoader.idl',
'nsIImageLoadingContent.idl',
'nsIMessageManager.idl',
'nsIObjectLoadingContent.idl',
+ 'nsIPerformanceMetrics.idl',
'nsIRemoteWindowContext.idl',
'nsIScriptChannel.idl',
'nsISelection.idl',
'nsISelectionController.idl',
'nsISelectionDisplay.idl',
'nsISelectionListener.idl',
'nsISelectionPrivate.idl',
'nsISlowScriptDebug.idl',
@@ -104,16 +105,17 @@ EXPORTS += [
'nsITimeoutHandler.h',
'nsJSEnvironment.h',
'nsJSUtils.h',
'nsLineBreaker.h',
'nsMappedAttributeElement.h',
'nsNameSpaceManager.h',
'nsNodeInfoManager.h',
'nsNodeUtils.h',
+ 'nsPerformanceMetrics.h',
'nsPIDOMWindow.h',
'nsPIDOMWindowInlines.h',
'nsPIWindowRoot.h',
'nsPropertyTable.h',
'nsRange.h',
'nsSandboxFlags.h',
'nsStructuredCloneContainer.h',
'nsStubAnimationObserver.h',
@@ -313,16 +315,17 @@ UNIFIED_SOURCES += [
'nsMappedAttributeElement.cpp',
'nsMappedAttributes.cpp',
'nsMimeTypeArray.cpp',
'nsNameSpaceManager.cpp',
'nsNoDataProtocolContentPolicy.cpp',
'nsNodeInfoManager.cpp',
'nsNodeUtils.cpp',
'nsOpenURIInFrameParams.cpp',
+ 'nsPerformanceMetrics.cpp',
'nsPlainTextSerializer.cpp',
'nsPropertyTable.cpp',
'nsQueryContentEventResult.cpp',
'nsRange.cpp',
'nsScreen.cpp',
'nsScriptNameSpaceManager.cpp',
'nsStructuredCloneContainer.cpp',
'nsStubAnimationObserver.cpp',
new file mode 100644
--- /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;
+};
+
+
new file mode 100644
--- /dev/null
+++ b/dom/base/nsPerformanceMetrics.cpp
@@ -0,0 +1,124 @@
+/* -*- 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 <nsIMutableArray.h>
+#include <nsArrayUtils.h>
+#include <nsPerformanceMetrics.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<nsIPerformanceMetricsDispatchCategory> 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<nsIMutableArray> 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;
+}
new file mode 100644
--- /dev/null
+++ b/dom/base/nsPerformanceMetrics.h
@@ -0,0 +1,46 @@
+/* -*- 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 "nsIPerformanceMetrics.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<nsIPerformanceMetricsDispatchCategory> mItems;
+};
+
+#endif // end nsPerformanceMetrics_h__
--- a/dom/chrome-webidl/ChromeUtils.webidl
+++ b/dom/chrome-webidl/ChromeUtils.webidl
@@ -118,16 +118,20 @@ namespace ChromeUtils {
readonly attribute any recentJSDevError;
/**
* Reset `recentJSDevError` to `undefined` for the current JSRuntime.
*/
void clearRecentJSDevError();
#endif // NIGHTLY_BUILD
+#ifndef RELEASE_OR_BETA
+ void requestPerformanceMetrics();
+#endif
+
/**
* IF YOU ADD NEW METHODS HERE, MAKE SURE THEY ARE THREAD-SAFE.
*/
};
/**
* Additional ChromeUtils methods that are _not_ thread-safe, and hence not
* exposed in workers.
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -38,16 +38,18 @@
#include "mozilla/dom/MemoryReportRequest.h"
#include "mozilla/dom/PLoginReputationChild.h"
#include "mozilla/dom/ProcessGlobal.h"
#include "mozilla/dom/PushNotifier.h"
#include "mozilla/dom/ServiceWorkerManager.h"
#include "mozilla/dom/TabGroup.h"
#include "mozilla/dom/nsIContentChild.h"
#include "mozilla/dom/URLClassifierChild.h"
+#include "mozilla/dom/WorkerDebugger.h"
+#include "mozilla/dom/WorkerDebuggerManager.h"
#include "mozilla/gfx/gfxVars.h"
#include "mozilla/gfx/Logging.h"
#include "mozilla/psm/PSMContentListener.h"
#include "mozilla/hal_sandbox/PHalChild.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/FileDescriptorSetChild.h"
#include "mozilla/ipc/FileDescriptorUtils.h"
#include "mozilla/ipc/GeckoChildProcessHost.h"
@@ -73,16 +75,19 @@
#include "nsBaseDragService.h"
#include "mozilla/media/MediaChild.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/WebBrowserPersistDocumentChild.h"
#include "mozilla/HangDetails.h"
#include "imgLoader.h"
#include "GMPServiceChild.h"
#include "NullPrincipal.h"
+#include "nsIPerformanceMetrics.h"
+#include "nsISimpleEnumerator.h"
+#include "nsIWorkerDebuggerManager.h"
#if !defined(XP_WIN)
#include "mozilla/Omnijar.h"
#endif
#ifdef MOZ_GECKO_PROFILER
#include "ChildProfilerController.h"
#endif
@@ -1369,16 +1374,48 @@ ContentChild::GetResultForRenderingInitF
// If we are talking to the GPU process, then we should recover from this on
// the next ContentChild::RecvReinitRendering call.
gfxCriticalNote << "Could not initialize rendering with GPU process";
return IPC_OK();
}
mozilla::ipc::IPCResult
+ContentChild::RecvRequestPerformanceMetrics()
+{
+#ifndef RELEASE_OR_BETA
+ // iterate on all WorkerDebugger
+ RefPtr<WorkerDebuggerManager> wdm = WorkerDebuggerManager::GetOrCreate();
+ if (NS_WARN_IF(!wdm)) {
+ return IPC_OK();
+ }
+
+ for (uint32_t index = 0; index < wdm->GetDebuggersLength(); index++) {
+ WorkerDebugger* debugger = wdm->GetDebuggerAt(index);
+ MOZ_ASSERT(debugger);
+ SendAddPerformanceMetrics(debugger->ReportPerformanceInfo());
+ }
+
+ // iterate on all DocGroup
+ nsTArray<RefPtr<TabChild>> tabs = TabChild::GetAll();
+ for (const auto& tabChild : tabs) {
+ TabGroup* tabGroup = tabChild->TabGroup();
+ for (auto iter = tabGroup->Iter(); !iter.Done(); iter.Next()) {
+ RefPtr<DocGroup> docGroup = iter.Get()->mDocGroup;
+ SendAddPerformanceMetrics(docGroup->ReportPerformanceInfo());
+ }
+ }
+ return IPC_OK();
+#endif
+#ifdef RELEASE_OR_BETA
+ return IPC_OK();
+#endif
+}
+
+mozilla::ipc::IPCResult
ContentChild::RecvInitRendering(Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces)
{
MOZ_ASSERT(namespaces.Length() == 3);
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -185,16 +185,19 @@ public:
RecvInitRendering(
Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces) override;
mozilla::ipc::IPCResult
+ RecvRequestPerformanceMetrics() override;
+
+ mozilla::ipc::IPCResult
RecvReinitRendering(
Endpoint<PCompositorManagerChild>&& aCompositor,
Endpoint<PImageBridgeChild>&& aImageBridge,
Endpoint<PVRManagerChild>&& aVRBridge,
Endpoint<PVideoDecoderManagerChild>&& aVideoManager,
nsTArray<uint32_t>&& namespaces) override;
virtual mozilla::ipc::IPCResult RecvAudioDefaultDeviceChange() override;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -187,16 +187,17 @@
#include "nsPluginHost.h"
#include "nsPluginTags.h"
#include "nsIBlocklistService.h"
#include "mozilla/StyleSheet.h"
#include "mozilla/StyleSheetInlines.h"
#include "nsHostObjectProtocolHandler.h"
#include "nsICaptivePortalService.h"
#include "nsIObjectLoadingContent.h"
+#include "nsPerformanceMetrics.h"
#include "nsIBidiKeyboard.h"
#include "nsLayoutStylesheetCache.h"
#include "ContentPrefs.h"
#include "mozilla/Sprintf.h"
@@ -3268,16 +3269,54 @@ ContentParent::RecvFinishMemoryReport(co
{
if (mMemoryReportRequest) {
mMemoryReportRequest->Finish(aGeneration);
mMemoryReportRequest = nullptr;
}
return IPC_OK();
}
+mozilla::ipc::IPCResult
+ContentParent::RecvAddPerformanceMetrics(const PerformanceInfo& aMetrics)
+{
+#ifndef RELEASE_OR_BETA
+ // converting the data we get from a child as a notification
+ if (aMetrics.items().IsEmpty()) {
+ return IPC_OK();
+ }
+
+ nsCOMPtr<nsIMutableArray> xpItems = do_CreateInstance(NS_ARRAY_CONTRACTID);
+ if (NS_WARN_IF(!xpItems)) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+
+ for (uint32_t i = 0; i<aMetrics.items().Length(); i++) {
+ const CategoryDispatch& entry = aMetrics.items()[i];
+ nsCOMPtr<nsIPerformanceMetricsDispatchCategory> item =
+ new PerformanceMetricsDispatchCategory(entry.category(),
+ entry.count());
+ xpItems->AppendElement(item);
+ }
+
+ nsCOMPtr<nsIPerformanceMetricsData> data =
+ new PerformanceMetricsData(aMetrics.pid(), aMetrics.wid(), aMetrics.pwid(),
+ aMetrics.host(), aMetrics.duration(),
+ aMetrics.worker(), xpItems);
+ nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
+ if (!obs) {
+ return IPC_FAIL_NO_REASON(this);
+ }
+ obs->NotifyObservers(data, "performance-metrics", nullptr);
+ return IPC_OK();
+#endif
+#ifdef RELEASE_OR_BETA
+ return IPC_OK();
+#endif
+}
+
PCycleCollectWithLogsParent*
ContentParent::AllocPCycleCollectWithLogsParent(const bool& aDumpAllTraces,
const FileDescriptor& aGCLog,
const FileDescriptor& aCCLog)
{
MOZ_CRASH("Don't call this; use ContentParent::CycleCollectWithLogs");
}
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -843,16 +843,17 @@ private:
* Get or create the corresponding content parent array to |aContentProcessType|.
*/
static nsTArray<ContentParent*>& GetOrCreatePool(const nsAString& aContentProcessType);
virtual mozilla::ipc::IPCResult RecvInitBackground(Endpoint<mozilla::ipc::PBackgroundParent>&& aEndpoint) override;
mozilla::ipc::IPCResult RecvAddMemoryReport(const MemoryReport& aReport) override;
mozilla::ipc::IPCResult RecvFinishMemoryReport(const uint32_t& aGeneration) override;
+ mozilla::ipc::IPCResult RecvAddPerformanceMetrics(const PerformanceInfo& aMetrics) override;
virtual bool
DeallocPJavaScriptParent(mozilla::jsipc::PJavaScriptParent*) override;
virtual bool
DeallocPRemoteSpellcheckEngineParent(PRemoteSpellcheckEngineParent*) override;
virtual PBrowserParent* AllocPBrowserParent(const TabId& aTabId,
--- a/dom/ipc/DOMTypes.ipdlh
+++ b/dom/ipc/DOMTypes.ipdlh
@@ -119,10 +119,48 @@ struct CreatedWindowInfo
nsCString urlToLoad;
TextureFactoryIdentifier textureFactoryIdentifier;
uint64_t layersId;
CompositorOptions compositorOptions;
uint32_t maxTouchPoints;
DimensionInfo dimensions;
};
+
+/**
+ * PerformanceInfo is used to pass performance info stored
+ * in WorkerPrivate & DocGroup instances
+ *
+ * Each (host, pid, wid, pwid) is unique to a given DocGroup or
+ * Worker, and we collect the number of dispatches per Dispatch
+ * category and total execution duration.
+ *
+ * This IPDL struct reflects the data collected in Performance counters.
+ * see xpcom/threads/PerformanceCounter.h
+ */
+struct CategoryDispatch
+{
+ // DispatchCategory value
+ uint16_t category;
+ // Number of dispatch
+ uint16_t count;
+};
+
+struct PerformanceInfo
+{
+ // Host of the document, if any
+ nsCString host;
+ // process id
+ uint16_t pid;
+ // window id
+ uint64_t wid;
+ // "parent" window id
+ uint64_t pwid;
+ // Execution time in microseconds
+ uint64_t duration;
+ // True if the data is collected in a worker
+ bool worker;
+ // Counters per category. For workers, a single entry
+ CategoryDispatch[] items;
+};
+
} // namespace dom
} // namespace mozilla
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -390,16 +390,17 @@ child:
* abnormally exit if this fails; the details are OS-specific.
*/
async SetProcessSandbox(MaybeFileDesc aBroker);
async RequestMemoryReport(uint32_t generation,
bool anonymize,
bool minimizeMemoryUsage,
MaybeFileDesc DMDFile);
+ async RequestPerformanceMetrics();
/**
* Communication between the PuppetBidiKeyboard and the actual
* BidiKeyboard hosted by the parent
*/
async BidiKeyboardNotify(bool isLangRTL, bool haveBidiKeyboards);
/**
@@ -1122,16 +1123,18 @@ parent:
IHandlerControlHolder aHandlerControl);
async AddMemoryReport(MemoryReport aReport);
async FinishMemoryReport(uint32_t aGeneration);
async MaybeReloadPlugins();
async BHRThreadHang(HangDetails aHangDetails);
+
+ async AddPerformanceMetrics(PerformanceInfo aMetrics);
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/dom/tests/browser/browser.ini
+++ b/dom/tests/browser/browser.ini
@@ -67,8 +67,9 @@ support-files =
support-files =
test_new_window_from_content_child.html
[browser_xhr_sandbox.js]
[browser_noopener.js]
support-files =
test_noopener_source.html
test_noopener_target.html
[browser_noopener_null_uri.js]
+[browser_test_performance_metrics.js]
new file mode 100644
--- /dev/null
+++ b/dom/tests/browser/browser_test_performance_metrics.js
@@ -0,0 +1,55 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ts=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/. */
+
+const TEST_URL = "http://example.com/browser/dom/tests/browser/dummy.html";
+ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
+
+
+add_task(async function test() {
+ if (!AppConstants.RELEASE_OR_BETA) {
+ SpecialPowers.setBoolPref('dom.performance.enable_scheduler_timing', true);
+ waitForExplicitFinish();
+
+ await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
+ async function(browser) {
+
+ // grab events..
+ var events = [];
+ function getInfoFromService(subject, topic, value) {
+ subject = subject.QueryInterface(Ci.nsIPerformanceMetricsData);
+ if (subject.host == "example.com") {
+ events.push(subject);
+ }
+ }
+ Services.obs.addObserver(getInfoFromService, "performance-metrics");
+
+ // trigger an IPDL call
+ ChromeUtils.requestPerformanceMetrics();
+
+ // wait until we get the events back
+ await BrowserTestUtils.waitForCondition(() => {
+ return events.length > 0;
+ }, "wait for events to come in", 100, 20);
+
+ // let's check the last example.com tab event we got
+ let last = events[0];
+ Assert.equal(last.host, "example.com", "host should be example.com");
+ Assert.ok(last.duration > 0, "Duration should be positive");
+
+ // let's look at the XPCOM data we got back
+ let items = last.items.QueryInterface(Ci.nsIMutableArray);
+ let enumerator = items.enumerate();
+ let total = 0;
+ while (enumerator.hasMoreElements()) {
+ let item = enumerator.getNext();
+ item = item.QueryInterface(Ci.nsIPerformanceMetricsDispatchCategory);
+ total += item.count;
+ }
+ Assert.ok(total > 0);
+ });
+ SpecialPowers.clearUserPref('dom.performance.enable_scheduler_timing');
+ }
+});
--- a/dom/workers/WorkerDebugger.cpp
+++ b/dom/workers/WorkerDebugger.cpp
@@ -12,16 +12,21 @@
#include "nsQueryObject.h"
#include "nsThreadUtils.h"
#include "ScriptLoader.h"
#include "WorkerCommon.h"
#include "WorkerError.h"
#include "WorkerPrivate.h"
#include "WorkerRunnable.h"
#include "WorkerScope.h"
+#if defined(XP_WIN)
+#include <processthreadsapi.h> // for GetCurrentProcessId()
+#else
+#include <unistd.h> // for getpid()
+#endif // defined(XP_WIN)
namespace mozilla {
namespace dom {
namespace {
class DebuggerMessageEventRunnable : public WorkerDebuggerRunnable
{
@@ -466,11 +471,49 @@ WorkerDebugger::ReportErrorToDebuggerOnM
}
WorkerErrorReport report;
report.mMessage = aMessage;
report.mFilename = aFilename;
WorkerErrorReport::LogErrorToConsole(report, 0);
}
+#ifndef RELEASE_OR_BETA
+PerformanceInfo
+WorkerDebugger::ReportPerformanceInfo()
+{
+ AssertIsOnMainThread();
+#if defined(XP_WIN)
+ uint32_t pid = GetCurrentProcessId();
+#else
+ uint32_t pid = getpid();
+#endif
+ uint64_t wid = mWorkerPrivate->WindowID();
+ uint64_t pwid = wid;
+ nsPIDOMWindowInner* win = mWorkerPrivate->GetWindow();
+ if (win) {
+ nsPIDOMWindowOuter* outer = win->GetOuterWindow();
+ if (outer) {
+ nsCOMPtr<nsPIDOMWindowOuter> top = outer->GetTop();
+ if (top) {
+ pwid = top->WindowID();
+ }
+ }
+ }
+ RefPtr<PerformanceCounter> perf = mWorkerPrivate->GetPerformanceCounter();
+ uint16_t count = perf->GetTotalDispatchCount();
+ uint64_t duration = perf->GetExecutionDuration();
+ RefPtr<nsIURI> uri = mWorkerPrivate->GetResolvedScriptURI();
+ CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
+ FallibleTArray<CategoryDispatch> items;
+ if (!items.AppendElement(item, fallible)) {
+ NS_ERROR("Could not complete the operation");
+ return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
+ true, items);
+ }
+ perf->ResetPerformanceCounters();
+ return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,
+ true, items);
+}
+#endif
} // dom namespace
} // mozilla namespace
--- a/dom/workers/WorkerDebugger.h
+++ b/dom/workers/WorkerDebugger.h
@@ -2,16 +2,17 @@
/* 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 mozilla_dom_workers_WorkerDebugger_h
#define mozilla_dom_workers_WorkerDebugger_h
+#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/WorkerCommon.h"
#include "nsIWorkerDebugger.h"
namespace mozilla {
namespace dom {
class WorkerPrivate;
@@ -38,16 +39,25 @@ public:
void
PostMessageToDebugger(const nsAString& aMessage);
void
ReportErrorToDebugger(const nsAString& aFilename, uint32_t aLineno,
const nsAString& aMessage);
+#ifndef RELEASE_OR_BETA
+ /*
+ * Sends back a PerformanceInfo struct from the counters
+ * in mWorkerPrivate. Counters are reset to zero after this call.
+ */
+ PerformanceInfo
+ ReportPerformanceInfo();
+#endif
+
private:
virtual
~WorkerDebugger();
void
PostMessageToDebuggerOnMainThread(const nsAString& aMessage);
void
--- a/dom/workers/WorkerDebuggerManager.cpp
+++ b/dom/workers/WorkerDebuggerManager.cpp
@@ -356,10 +356,22 @@ WorkerDebuggerManager::UnregisterDebugge
for (size_t index = 0; index < listeners.Length(); ++index) {
listeners[index]->OnUnregister(debugger);
}
debugger->Close();
aWorkerPrivate->SetIsDebuggerRegistered(false);
}
+uint32_t
+WorkerDebuggerManager::GetDebuggersLength() const
+{
+ return mDebuggers.Length();
+}
+
+WorkerDebugger*
+WorkerDebuggerManager::GetDebuggerAt(uint32_t aIndex) const
+{
+ return mDebuggers.SafeElementAt(aIndex, nullptr);
+}
+
} // dom namespace
} // mozilla namespace
--- a/dom/workers/WorkerDebuggerManager.h
+++ b/dom/workers/WorkerDebuggerManager.h
@@ -69,16 +69,22 @@ public:
bool aNotifyListeners);
void
UnregisterDebugger(WorkerPrivate* aWorkerPrivate);
void
UnregisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate);
+ uint32_t
+ GetDebuggersLength() const;
+
+ WorkerDebugger*
+ GetDebuggerAt(uint32_t aIndex) const;
+
private:
virtual ~WorkerDebuggerManager();
};
inline nsresult
RegisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
{
WorkerDebuggerManager* manager;