Bug 1470073 - Make IOActivityMonitor timer optional - r=valentin

- Introduced the io.activity.enabled pref, so IOActivityMonitor can run without a timer
- Added IOActivityMonitor::NotifyActivities() to trigger notifications manually
- Added ChromeUtils.requestIOActivity() so we can trigger it via JS

MozReview-Commit-ID: 9JA2rLaM496

--HG--
extra : rebase_source : e473a7b0ec7c231ab321846c5ddcc4d6a88d7245
This commit is contained in:
Tarek Ziadé 2018-06-26 10:43:16 +02:00
Родитель 1808cc5dec
Коммит 7158cbfa29
9 изменённых файлов: 127 добавлений и 39 удалений

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

@ -13,11 +13,13 @@
#include "mozilla/BasePrincipal.h"
#include "mozilla/CycleCollectedJSRuntime.h"
#include "mozilla/PerformanceUtils.h"
#include "mozilla/Preferences.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 "IOActivityMonitor.h"
#include "nsThreadUtils.h"
#include "mozJSComponentLoader.h"
#include "GeckoProfiler.h"
@ -767,5 +769,13 @@ ChromeUtils::CreateError(const GlobalObject& aGlobal, const nsAString& aMessage,
aRetVal.set(retVal);
}
/* static */ void
ChromeUtils::RequestIOActivity(GlobalObject&)
{
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false));
mozilla::Unused << mozilla::net::IOActivityMonitor::NotifyActivities();
}
} // namespace dom
} // namespace mozilla

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

@ -182,6 +182,8 @@ public:
CreateError(const GlobalObject& global, const nsAString& message,
JS::Handle<JSObject*> stack,
JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv);
static void RequestIOActivity(GlobalObject& aGlobal);
};
} // namespace dom

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

@ -348,6 +348,11 @@ partial namespace ChromeUtils {
* Request performance metrics to the current process & all ontent processes.
*/
void requestPerformanceMetrics();
/**
* Request IOActivityMonitor to send a notification containing I/O activity
*/
void requestIOActivity();
};
/**

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

@ -5127,12 +5127,17 @@ pref("memory.blob_report.stack_frames", 0);
// observers (bug 780507).
pref("dom.idle-observers-api.fuzz_time.disabled", true);
// Activates the activity monitor
pref("io.activity.enabled", false);
// Minimum delay in milliseconds between I/O activity notifications (0 means
// no notifications). I/O activity includes socket and disk files.
//
// The delay is the same for both read and write, though
// they are handled separately. This pref is only read once at startup:
// a restart is required to enable a new value.
//
// io.activity.enabled needs to be set to true
pref("io.activity.intervalMilliseconds", 0);
// If true, reuse the same global for (almost) everything loaded by the component

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

@ -382,6 +382,23 @@ IOActivityMonitor::IOActivityMonitor()
NS_IMETHODIMP
IOActivityMonitor::Notify(nsITimer* aTimer)
{
return NotifyActivities();
}
// static
nsresult
IOActivityMonitor::NotifyActivities()
{
RefPtr<IOActivityMonitor> mon(gInstance);
if (!IsActive()) {
return NS_ERROR_FAILURE;
}
return mon->NotifyActivities_Internal();
}
nsresult
IOActivityMonitor::NotifyActivities_Internal()
{
mozilla::MutexAutoLock lock(mLock);
nsCOMPtr<nsIRunnable> ev = NotifyIOActivity::Create(mActivities, lock);
@ -451,8 +468,15 @@ IOActivityMonitor::Init_Internal(int32_t aInterval)
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
}
// create and fire the timer
mInterval = aInterval;
// if the interval is 0, the timer is not fired
// and calls are done explicitely via NotifyActivities
if (mInterval == 0) {
return NS_OK;
}
// create and fire the timer
mTimer = NS_NewTimer();
if (!mTimer) {
return NS_ERROR_FAILURE;
@ -474,7 +498,9 @@ nsresult
IOActivityMonitor::Shutdown_Internal()
{
mozilla::MutexAutoLock lock(mLock);
mTimer->Cancel();
if (mTimer) {
mTimer->Cancel();
}
mActivities.Clear();
gInstance = nullptr;
return NS_OK;

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

@ -22,6 +22,9 @@
namespace mozilla { namespace net {
#define IO_ACTIVITY_ENABLED_PREF "io.activity.enabled"
#define IO_ACTIVITY_INTERVAL_PREF "io.activity.intervalMilliseconds"
//
// IOActivity keeps track of the amount of data
// sent and received for an FD / Location
@ -98,6 +101,10 @@ public:
static nsresult Write(PRFileDesc *fd, uint32_t aAmount);
static bool IsActive();
// collects activities and notifies observers
// this method can be called manually or via the timer callback
static nsresult NotifyActivities();
private:
virtual ~IOActivityMonitor() = default;
nsresult Init_Internal(int32_t aInterval);
@ -106,6 +113,7 @@ private:
IOActivity* GetActivity(const nsACString& location);
nsresult Write_Internal(const nsACString& location, uint32_t aAmount);
nsresult Read_Internal(const nsACString& location, uint32_t aAmount);
nsresult NotifyActivities_Internal();
Activities mActivities;

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

@ -49,11 +49,16 @@ interface nsPISocketTransportService : nsIRoutedSocketTransportService
%{C++
/*
* I/O activity: we send out this topic no more than every
* intervalMilliseconds (as set by the
* "io.activity.intervalMilliseconds" preference: if 0 no notifications
* are sent) if the I/O is currently active (i.e. we're sending/receiving
* data to/from the socket or writing/reading to/from a file).
* I/O activity observer topic. Sends out information about the
* amount of data we're sending/receiving via sockets and disk files.
*
* Activated via the "io.activity.enabled" preference.
*
* when "io.activity.intervalMilliseconds" is > 0, automatically sends
* out this topic no more than every intervalMilliseconds.
*
* if "io.activity.intervalMilliseconds" is 0, notifications are triggered
* manually via IOActivityMonitor::NotifyActivities()
*/
#define NS_IO_ACTIVITY "io-activity"

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

@ -49,7 +49,6 @@ static Atomic<PRThread*, Relaxed> gSocketThread;
#define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count"
#define SOCKET_LIMIT_TARGET 1000U
#define SOCKET_LIMIT_MIN 50U
#define IO_ACTIVITY_INTERVAL_PREF "io.activity.intervalMilliseconds"
#define MAX_TIME_BETWEEN_TWO_POLLS "network.sts.max_time_for_events_between_two_polls"
#define POLL_BUSY_WAIT_PERIOD "network.sts.poll_busy_wait_period"
#define POLL_BUSY_WAIT_PERIOD_TIMEOUT "network.sts.poll_busy_wait_period_timeout"
@ -1446,12 +1445,10 @@ nsSocketTransportService::Observe(nsISupports *subject,
}
if (!strcmp(topic, "profile-initial-state")) {
int32_t interval = Preferences::GetInt(IO_ACTIVITY_INTERVAL_PREF, 0);
if (interval <= 0) {
return NS_OK;
if (!Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false)) {
return NS_OK;
}
return net::IOActivityMonitor::Init(interval);
return net::IOActivityMonitor::Init(Preferences::GetInt(IO_ACTIVITY_INTERVAL_PREF, 0));
}
if (!strcmp(topic, "last-pb-context-exited")) {

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

@ -7,47 +7,77 @@
const TEST_URL = "http://example.com/browser/dom/tests/browser/dummy.html";
add_task(async function test() {
var gotSocket = false;
var gotFile = false;
var gotSqlite = false;
var gotEmptyData = false;
var networkActivity = function(subject, topic, value) {
subject.QueryInterface(Ci.nsIMutableArray);
let enumerator = subject.enumerate();
while (enumerator.hasMoreElements()) {
let data = enumerator.getNext();
data = data.QueryInterface(Ci.nsIIOActivityData);
gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData
gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket;
gotFile = data.location.endsWith(".js") || gotFile;
gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite;
}
};
SpecialPowers.setIntPref('io.activity.intervalMilliseconds', 50);
waitForExplicitFinish();
// grab events..
let gotSocket = false;
let gotFile = false;
let gotSqlite = false;
let gotEmptyData = false;
let networkActivity = function(subject, topic, value) {
subject.QueryInterface(Ci.nsIMutableArray);
let enumerator = subject.enumerate();
while (enumerator.hasMoreElements()) {
let data = enumerator.getNext();
data = data.QueryInterface(Ci.nsIIOActivityData);
gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData
gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket;
gotFile = data.location.endsWith(".js") || gotFile;
gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite;
}
};
function startObserver() {
gotSocket = gotFile = gotSqlite = gotEmptyData = false;
Services.obs.addObserver(networkActivity, "io-activity");
// why do I have to do this ??
Services.obs.notifyObservers(null, "profile-initial-state", null);
}
// this test activates the timer and checks the results as they come in
add_task(async function testWithTimer() {
await SpecialPowers.pushPrefEnv({
"set": [
["io.activity.enabled", true],
["io.activity.intervalMilliseconds", 50]
]
});
waitForExplicitFinish();
startObserver();
await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
async function(browser) {
// wait until we get the events back
await BrowserTestUtils.waitForCondition(() => {
return gotSocket && gotFile && gotSqlite && !gotEmptyData;
}, "wait for events to come in", 250, 5);
}, "wait for events to come in", 500);
ok(gotSocket, "A socket was used");
ok(gotFile, "A file was used");
ok(gotSqlite, "A sqlite DB was used");
ok(!gotEmptyData, "Every I/O event had data");
});
});
// this test manually triggers notifications via ChromeUtils.requestIOActivity()
add_task(async function testWithManualCall() {
await SpecialPowers.pushPrefEnv({
"set": [
["io.activity.enabled", true],
]
});
waitForExplicitFinish();
startObserver();
await BrowserTestUtils.withNewTab({ gBrowser, url: "http://example.com" },
async function(browser) {
// wait until we get the events back
await BrowserTestUtils.waitForCondition(() => {
ChromeUtils.requestIOActivity();
return gotSocket && gotFile && gotSqlite && !gotEmptyData;
}, "wait for events to come in", 500);
ok(gotSocket, "A socket was used");
ok(gotFile, "A file was used");
ok(gotSqlite, "A sqlite DB was used");
ok(!gotEmptyData, "Every I/O event had data");
});
SpecialPowers.clearUserPref('io.activity.intervalMilliseconds');
});