Bug 1447768 - part 2 - Dispatch counters in the parent process - r=baku

Chromeutils.RequestPerformanceMetrics() is now composed of two parts:
- calls content processes via IPDL to get their counters
- directly dispatch counters from the parent process

MozReview-Commit-ID: HlgcEOzkyAq

--HG--
extra : rebase_source : 60e81a27cd3a1bf1378e6b977529964507633b63
This commit is contained in:
Tarek Ziadé 2018-04-04 13:36:25 +02:00
Родитель 0cc1d7259a
Коммит f9138cc6f4
10 изменённых файлов: 165 добавлений и 38 удалений

Просмотреть файл

@ -12,6 +12,9 @@
#include "mozilla/Base64.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#ifndef RELEASE_OR_BETA
#include "mozilla/PerformanceUtils.h"
#endif
#include "mozilla/TimeStamp.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/IdleDeadline.h"
@ -659,11 +662,25 @@ ChromeUtils::ClearRecentJSDevError(GlobalObject&)
ChromeUtils::RequestPerformanceMetrics(GlobalObject&)
{
MOZ_ASSERT(XRE_IsParentProcess());
// calling all content processes via IPDL (async)
nsTArray<ContentParent*> children;
ContentParent::GetAll(children);
for (uint32_t i = 0; i < children.Length(); i++) {
mozilla::Unused << children[i]->SendRequestPerformanceMetrics();
}
// collecting the current process counters and notifying them
nsTArray<PerformanceInfo> info;
CollectPerformanceInfo(info);
SystemGroup::Dispatch(TaskCategory::Performance,
NS_NewRunnableFunction(
"RequestPerformanceMetrics",
[info]() { mozilla::Unused << NS_WARN_IF(NS_FAILED(NotifyPerformanceInfo(info))); }
)
);
}
#endif

Просмотреть файл

@ -150,7 +150,7 @@ struct PerformanceInfo
// Host of the document, if any
nsCString host;
// process id
uint16_t pid;
uint32_t pid;
// window id
uint64_t wid;
// "parent" window id

Просмотреть файл

@ -9,6 +9,7 @@ support-files =
worker_bug1004814.js
geo_leak_test.html
dummy.html
ping_worker.html
test_largeAllocation.html
test_largeAllocation.html^headers^
test_largeAllocation2.html

Просмотреть файл

@ -4,52 +4,113 @@
* 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");
const ROOT_URL = "http://example.com/browser/dom/tests/browser";
const DUMMY_URL = ROOT_URL + "/dummy.html";
const WORKER_URL = ROOT_URL + "/ping_worker.html";
let nextId = 0;
function jsonrpc(tab, method, params) {
let currentId = nextId++;
let messageManager = tab.linkedBrowser.messageManager;
messageManager.sendAsyncMessage("jsonrpc", {
id: currentId,
method: method,
params: params
});
return new Promise(function (resolve, reject) {
messageManager.addMessageListener("jsonrpc", function listener(event) {
let { id, result, error } = event.data;
if (id !== currentId) {
return;
}
messageManager.removeMessageListener("jsonrpc", listener);
if (error) {
reject(error);
return;
}
resolve(result);
});
});
}
function postMessageToWorker(tab, message) {
return jsonrpc(tab, "postMessageToWorker", [WORKER_URL, message]);
}
add_task(async function test() {
if (!AppConstants.RELEASE_OR_BETA) {
SpecialPowers.setBoolPref('dom.performance.enable_scheduler_timing', true);
waitForExplicitFinish();
SpecialPowers.setBoolPref('dom.performance.enable_scheduler_timing', true);
waitForExplicitFinish();
await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
async function(browser) {
// Load 3 pages and wait. The 3rd one has a worker
let page1 = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: 'about:about', forceNewProcess: false
});
// grab events..
var events = [];
function getInfoFromService(subject, topic, value) {
subject = subject.QueryInterface(Ci.nsIPerformanceMetricsData);
if (subject.host == "example.com") {
events.push(subject);
let page2 = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: 'about:memory', forceNewProcess: false
});
let page3 = await BrowserTestUtils.openNewForegroundTab({
gBrowser, opening: "about:performance", forceNewProcess: true
});
let parent_process_event = false;
let worker_event = false;
// load a 4th tab with a worker
await BrowserTestUtils.withNewTab({ gBrowser, url: WORKER_URL },
async function(browser) {
// grab events..
var events = [];
function getInfoFromService(subject, topic, value) {
subject = subject.QueryInterface(Ci.nsIMutableArray);
let enumerator = subject.enumerate();
while (enumerator.hasMoreElements()) {
let item = enumerator.getNext();
item = item.QueryInterface(Ci.nsIPerformanceMetricsData);
if (item.pid == Services.appinfo.processID) {
parent_process_event = true;
}
if (item.worker) {
worker_event = true;
}
events.push(item);
}
Services.obs.addObserver(getInfoFromService, "performance-metrics");
}
// trigger an IPDL call
Services.obs.addObserver(getInfoFromService, "performance-metrics");
// wait until we get some events back by triggering requestPerformanceMetrics
await BrowserTestUtils.waitForCondition(() => {
ChromeUtils.requestPerformanceMetrics();
return events.length > 10;
}, "wait for events to come in", 500, 10);
// 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");
BrowserTestUtils.removeTab(page1);
BrowserTestUtils.removeTab(page2);
BrowserTestUtils.removeTab(page3);
// let's check the events
let duration = 0;
let total = 0;
for (let i=0; i < events.length; i++) {
duration += events[i].duration;
// let's look at the XPCOM data we got back
let items = last.items.QueryInterface(Ci.nsIMutableArray);
let items = events[i].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');
}
}
Assert.ok(duration > 0, "Duration should be positive");
Assert.ok(total > 0, "Should get a positive count");
Assert.ok(parent_process_event, "parent process sent back some events");
});
SpecialPowers.clearUserPref('dom.performance.enable_scheduler_timing');
});

Просмотреть файл

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<script type="text/javascript">
var myWorker;
function init() {
myWorker = new Worker('ping_worker.js');
myWorker.postMessage("ping");
}
</script>
</head>
<body onload="init()">
<h1>A page with a worker</h1>
</body>
</html>

Просмотреть файл

@ -0,0 +1,11 @@
/* 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/. */
"use strict";
function messageListener(event) {
postMessage("pong");
}
addEventListener("message", { handleEvent: messageListener });

Просмотреть файл

@ -502,12 +502,16 @@ WorkerDebugger::ReportPerformanceInfo()
uint16_t count = perf->GetTotalDispatchCount();
uint64_t duration = perf->GetExecutionDuration();
RefPtr<nsIURI> uri = mWorkerPrivate->GetResolvedScriptURI();
CategoryDispatch item = CategoryDispatch(DispatchCategory::Worker.GetValue(), count);
// 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<CategoryDispatch> items;
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);
true, items);
}
perf->ResetPerformanceCounters();
return PerformanceInfo(uri->GetSpecOrDefault(), pid, wid, pwid, duration,

Просмотреть файл

@ -424,10 +424,14 @@ private:
return false;
}
// PerformanceStorage needs to be initialized on the worker thread before
// being used on main-thread. Let's be sure that it is created before any
// PerformanceStorage & PerformanceCounter both need to be initialized
// on the worker thread before being used on main-thread.
// Let's be sure that it is created before any
// content loading.
aWorkerPrivate->EnsurePerformanceStorage();
#ifndef RELEASE_OR_BETA
aWorkerPrivate->EnsurePerformanceCounter();
#endif
ErrorResult rv;
workerinternals::LoadMainScript(aWorkerPrivate, mScriptURL, WorkerScript, rv);
@ -5210,15 +5214,19 @@ WorkerPrivate::DumpCrashInformation(nsACString& aString)
}
#ifndef RELEASE_OR_BETA
PerformanceCounter*
WorkerPrivate::GetPerformanceCounter()
void
WorkerPrivate::EnsurePerformanceCounter()
{
AssertIsOnWorkerThread();
if (!mPerformanceCounter) {
mPerformanceCounter = new PerformanceCounter(NS_ConvertUTF16toUTF8(mWorkerName));
}
}
PerformanceCounter*
WorkerPrivate::GetPerformanceCounter()
{
MOZ_ASSERT(mPerformanceCounter);
return mPerformanceCounter;
}
#endif

Просмотреть файл

@ -561,6 +561,11 @@ public:
void
EnsurePerformanceStorage();
#ifndef RELEASE_OR_BETA
void
EnsurePerformanceCounter();
#endif
const ClientInfo&
GetClientInfo() const;

Просмотреть файл

@ -36,6 +36,9 @@ enum class TaskCategory {
// Most DOM events (postMessage, media, plugins)
Other,
// Runnables related to Performance Counting
Performance,
Count
};