зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1491816
- deal with unresponsive content processes in ChromeUtils.requestPerformanceMetrics() - r=baku
Adds a timout that will resolve the promise to return even if we did not get an answer from all children. MozReview-Commit-ID: FFLwAUkkYos Differential Revision: https://phabricator.services.mozilla.com/D7265 --HG-- extra : moz-landing-system : lando
This commit is contained in:
Родитель
1c08e312af
Коммит
06ff704ff3
|
@ -1,5 +1,8 @@
|
|||
[DEFAULT]
|
||||
prefs = dom.performance.enable_scheduler_timing=true
|
||||
prefs =
|
||||
dom.performance.enable_scheduler_timing=true
|
||||
dom.performance.children_results_ipc_timeout=500
|
||||
|
||||
support-files =
|
||||
dummy.html
|
||||
ping_worker.html
|
||||
|
@ -7,6 +10,10 @@ support-files =
|
|||
ping_worker.js
|
||||
setinterval.html
|
||||
settimeout.html
|
||||
unresponsive.html
|
||||
|
||||
[browser_test_performance_metrics.js]
|
||||
skip-if = verify
|
||||
|
||||
[browser_test_unresponsive.js]
|
||||
skip-if = verify
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
/* -*- 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 ROOT_URL = "http://example.com/browser/dom/tests/browser/perfmetrics";
|
||||
const PAGE_URL = ROOT_URL + "/unresponsive.html";
|
||||
|
||||
add_task(async function test() {
|
||||
// dom.performance.enable_scheduler_timing is set to true in browser.ini
|
||||
waitForExplicitFinish();
|
||||
|
||||
await BrowserTestUtils.withNewTab({ gBrowser, url: PAGE_URL },
|
||||
async function(browser) {
|
||||
let dataBack = 0;
|
||||
let tabId = gBrowser.selectedBrowser.outerWindowID;
|
||||
|
||||
function exploreResults(data, filterByWindowId) {
|
||||
for (let entry of data) {
|
||||
if (entry.windowId == tabId && entry.host != "about:blank") {
|
||||
dataBack += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
let results = await ChromeUtils.requestPerformanceMetrics();
|
||||
exploreResults(results);
|
||||
Assert.ok(dataBack == 0);
|
||||
});
|
||||
|
||||
});
|
|
@ -0,0 +1,21 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en" dir="ltr">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<script type="text/javascript">
|
||||
|
||||
function fn() {
|
||||
let start = Date.now();
|
||||
while (Date.now() - start < 5000)
|
||||
; // do nothing
|
||||
setTimeout(fn, 0);
|
||||
}
|
||||
|
||||
setTimeout(fn, 10);
|
||||
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>An unresponsive page</h1>
|
||||
</body>
|
||||
</html>
|
|
@ -211,6 +211,12 @@ VARCACHE_PREF(
|
|||
RelaxedAtomicBool, true
|
||||
)
|
||||
|
||||
VARCACHE_PREF(
|
||||
"dom.performance.children_results_ipc_timeout",
|
||||
dom_performance_children_results_ipc_timeout,
|
||||
uint32_t, 1000
|
||||
)
|
||||
|
||||
// If true. then the service worker interception and the ServiceWorkerManager
|
||||
// will live in the parent process. This only takes effect on browser start.
|
||||
// Note, this is not currently safe to use for normal browsing yet.
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/PerformanceUtils.h"
|
||||
#include "mozilla/PerformanceMetricsCollector.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/WorkerDebugger.h"
|
||||
|
@ -24,6 +25,62 @@ static mozilla::LazyLogModule sPerfLog("PerformanceMetricsCollector");
|
|||
|
||||
namespace mozilla {
|
||||
|
||||
//
|
||||
// class IPCTimeout
|
||||
//
|
||||
NS_IMPL_ISUPPORTS(IPCTimeout, nsIObserver)
|
||||
|
||||
|
||||
// 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_NewTimerWithObserver(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::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(strcmp(aTopic, NS_TIMER_CALLBACK_TOPIC) == 0);
|
||||
LOG(("IPCTimeout timer triggered"));
|
||||
mResults->ResolveNow();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//
|
||||
// class AggregatedResults
|
||||
//
|
||||
|
@ -37,6 +94,7 @@ AggregatedResults::AggregatedResults(nsID aUUID,
|
|||
{
|
||||
MOZ_ASSERT(aCollector);
|
||||
MOZ_ASSERT(aPromise);
|
||||
mIPCTimeout = IPCTimeout::CreateInstance(this);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -44,11 +102,25 @@ AggregatedResults::Abort(nsresult aReason)
|
|||
{
|
||||
MOZ_ASSERT(mPromise);
|
||||
MOZ_ASSERT(NS_FAILED(aReason));
|
||||
if (mIPCTimeout) {
|
||||
mIPCTimeout->Cancel();
|
||||
mIPCTimeout = nullptr;
|
||||
}
|
||||
mPromise->MaybeReject(aReason);
|
||||
mPromise = nullptr;
|
||||
mPendingResults = 0;
|
||||
}
|
||||
|
||||
void
|
||||
AggregatedResults::ResolveNow()
|
||||
{
|
||||
MOZ_ASSERT(mPromise);
|
||||
LOG(("[%s] Early resolve", nsIDToCString(mUUID).get()));
|
||||
mPromise->MaybeResolve(mData);
|
||||
mIPCTimeout = nullptr;
|
||||
mCollector->ForgetAggregatedResults(mUUID);
|
||||
}
|
||||
|
||||
void
|
||||
AggregatedResults::AppendResult(const nsTArray<dom::PerformanceInfo>& aMetrics)
|
||||
{
|
||||
|
@ -97,6 +169,10 @@ AggregatedResults::AppendResult(const nsTArray<dom::PerformanceInfo>& aMetrics)
|
|||
}
|
||||
|
||||
LOG(("[%s] All data collected, resolving promise", nsIDToCString(mUUID).get()));
|
||||
if (mIPCTimeout) {
|
||||
mIPCTimeout->Cancel();
|
||||
mIPCTimeout = nullptr;
|
||||
}
|
||||
mPromise->MaybeResolve(mData);
|
||||
mCollector->ForgetAggregatedResults(mUUID);
|
||||
}
|
||||
|
@ -197,7 +273,13 @@ nsresult
|
|||
PerformanceMetricsCollector::DataReceived(const nsID& aUUID,
|
||||
const nsTArray<PerformanceInfo>& aMetrics)
|
||||
{
|
||||
MOZ_ASSERT(gInstance);
|
||||
// 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);
|
||||
}
|
||||
|
@ -209,6 +291,7 @@ PerformanceMetricsCollector::DataReceivedInternal(const nsID& aUUID,
|
|||
MOZ_ASSERT(gInstance == this);
|
||||
UniquePtr<AggregatedResults>* results = mAggregatedResults.GetValue(aUUID);
|
||||
if (!results) {
|
||||
LOG(("[%s] UUID is gone from mAggregatedResults", nsIDToCString(aUUID).get()));
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
#ifndef PerformanceMetricsCollector_h
|
||||
#define PerformanceMetricsCollector_h
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsID.h"
|
||||
#include "mozilla/dom/ChromeUtilsBinding.h" // defines PerformanceInfoDictionary
|
||||
#include "mozilla/dom/DOMTypes.h" // defines PerformanceInfo
|
||||
|
@ -17,6 +19,23 @@ namespace dom {
|
|||
}
|
||||
|
||||
class PerformanceMetricsCollector;
|
||||
class AggregatedResults;
|
||||
|
||||
class IPCTimeout final: public nsIObserver
|
||||
{
|
||||
public:
|
||||
NS_DECL_NSIOBSERVER
|
||||
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
|
||||
// via IPDL from all content processes and the main process. They
|
||||
|
@ -38,8 +57,10 @@ public:
|
|||
void AppendResult(const nsTArray<dom::PerformanceInfo>& aMetrics);
|
||||
void SetNumResultsRequired(uint32_t aNumResultsRequired);
|
||||
void Abort(nsresult aReason);
|
||||
void ResolveNow();
|
||||
|
||||
private:
|
||||
RefPtr<IPCTimeout> mIPCTimeout;
|
||||
RefPtr<dom::Promise> mPromise;
|
||||
uint32_t mPendingResults;
|
||||
FallibleTArray<dom::PerformanceInfoDictionary> mData;
|
||||
|
|
Загрузка…
Ссылка в новой задаче