forked from mirrors/gecko-dev
Bug 1472718 - Convert ChromeUtils.requestIOActivity to a Promise - r=baku,valentin
Changes: - The API now returns a Promise containing a sequence of IOActivityData dictionnaries. - All the code related to notifications and XPCOM is removed. - The counters are no longer reset to 0 when the API is called MozReview-Commit-ID: 7J2EgFqDgf --HG-- extra : rebase_source : eb7dc3e0921b12bbb3715a90863dc8e2a60c1c09
This commit is contained in:
parent
c76354430a
commit
8533ddcaac
11 changed files with 143 additions and 316 deletions
|
|
@ -769,12 +769,20 @@ ChromeUtils::CreateError(const GlobalObject& aGlobal, const nsAString& aMessage,
|
||||||
aRetVal.set(retVal);
|
aRetVal.set(retVal);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* static */ void
|
/* static */ already_AddRefed<Promise>
|
||||||
ChromeUtils::RequestIOActivity(GlobalObject&)
|
ChromeUtils::RequestIOActivity(GlobalObject& aGlobal, ErrorResult& aRv)
|
||||||
{
|
{
|
||||||
MOZ_ASSERT(XRE_IsParentProcess());
|
MOZ_ASSERT(XRE_IsParentProcess());
|
||||||
MOZ_ASSERT(Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false));
|
MOZ_ASSERT(Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false));
|
||||||
mozilla::Unused << mozilla::net::IOActivityMonitor::NotifyActivities();
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports());
|
||||||
|
MOZ_ASSERT(global);
|
||||||
|
RefPtr<Promise> domPromise = Promise::Create(global, aRv);
|
||||||
|
if (NS_WARN_IF(aRv.Failed())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
MOZ_ASSERT(domPromise);
|
||||||
|
mozilla::net::IOActivityMonitor::RequestActivities(domPromise);
|
||||||
|
return domPromise.forget();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
|
||||||
|
|
@ -183,7 +183,8 @@ public:
|
||||||
JS::Handle<JSObject*> stack,
|
JS::Handle<JSObject*> stack,
|
||||||
JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv);
|
JS::MutableHandle<JSObject*> aRetVal, ErrorResult& aRv);
|
||||||
|
|
||||||
static void RequestIOActivity(GlobalObject& aGlobal);
|
static already_AddRefed<Promise>
|
||||||
|
RequestIOActivity(GlobalObject& aGlobal, ErrorResult& aRv);
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace dom
|
} // namespace dom
|
||||||
|
|
|
||||||
|
|
@ -350,9 +350,23 @@ partial namespace ChromeUtils {
|
||||||
void requestPerformanceMetrics();
|
void requestPerformanceMetrics();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request IOActivityMonitor to send a notification containing I/O activity
|
* Returns a Promise containing a sequence of I/O activities
|
||||||
*/
|
*/
|
||||||
void requestIOActivity();
|
[Throws]
|
||||||
|
Promise<sequence<IOActivityDataDictionary>> requestIOActivity();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used by requestIOActivity() to return the number of bytes
|
||||||
|
* that were read (rx) and/or written (tx) for a given location.
|
||||||
|
*
|
||||||
|
* Locations can be sockets or files.
|
||||||
|
*/
|
||||||
|
dictionary IOActivityDataDictionary {
|
||||||
|
ByteString location = "";
|
||||||
|
unsigned long long rx = 0;
|
||||||
|
unsigned long long tx = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,14 @@
|
||||||
#include "nsSocketTransport2.h"
|
#include "nsSocketTransport2.h"
|
||||||
#include "nsSocketTransportService2.h"
|
#include "nsSocketTransportService2.h"
|
||||||
#include "nsThreadUtils.h"
|
#include "nsThreadUtils.h"
|
||||||
|
#include "mozilla/dom/Promise.h"
|
||||||
#include "mozilla/Services.h"
|
#include "mozilla/Services.h"
|
||||||
#include "prerror.h"
|
#include "prerror.h"
|
||||||
#include "prio.h"
|
#include "prio.h"
|
||||||
#include "prmem.h"
|
#include "prmem.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
using namespace mozilla;
|
||||||
using namespace mozilla::net;
|
using namespace mozilla::net;
|
||||||
|
|
||||||
mozilla::StaticRefPtr<IOActivityMonitor> gInstance;
|
mozilla::StaticRefPtr<IOActivityMonitor> gInstance;
|
||||||
|
|
@ -277,146 +279,58 @@ nsNetMon_AcceptRead(PRFileDesc *listenSock,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
|
||||||
// Class IOActivityData
|
|
||||||
//
|
|
||||||
NS_IMPL_ISUPPORTS(IOActivityData, nsIIOActivityData);
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
IOActivityData::GetLocation(nsACString& aLocation) {
|
|
||||||
aLocation = mActivity.location;
|
|
||||||
return NS_OK;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
IOActivityData::GetRx(int32_t* aRx) {
|
|
||||||
*aRx = mActivity.rx;
|
|
||||||
return NS_OK;
|
|
||||||
};
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
IOActivityData::GetTx(int32_t* aTx) {
|
|
||||||
*aTx = mActivity.tx;
|
|
||||||
return NS_OK;
|
|
||||||
};
|
|
||||||
|
|
||||||
//
|
|
||||||
// Class NotifyIOActivity
|
|
||||||
//
|
|
||||||
// Runnable that takes the activities per FD and location
|
|
||||||
// and converts them into IOActivity elements.
|
|
||||||
//
|
|
||||||
// These elements get notified.
|
|
||||||
//
|
|
||||||
class NotifyIOActivity : public mozilla::Runnable {
|
|
||||||
|
|
||||||
public:
|
|
||||||
static already_AddRefed<nsIRunnable>
|
|
||||||
Create(Activities& aActivities, const mozilla::MutexAutoLock& aProofOfLock)
|
|
||||||
{
|
|
||||||
RefPtr<NotifyIOActivity> runnable = new NotifyIOActivity();
|
|
||||||
|
|
||||||
for (auto iter = aActivities.Iter(); !iter.Done(); iter.Next()) {
|
|
||||||
IOActivity* activity = iter.Data();
|
|
||||||
if (!activity->Inactive()) {
|
|
||||||
if (NS_WARN_IF(!runnable->mActivities.AppendElement(*activity, mozilla::fallible))) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nsCOMPtr<nsIRunnable> result(runnable);
|
|
||||||
return result.forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
Run() override
|
|
||||||
{
|
|
||||||
MOZ_ASSERT(NS_IsMainThread());
|
|
||||||
if (mActivities.Length() == 0) {
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
|
||||||
if (!obs) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsCOMPtr<nsIMutableArray> array = do_CreateInstance(NS_ARRAY_CONTRACTID);
|
|
||||||
if (NS_WARN_IF(!array)) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned long i = 0; i < mActivities.Length(); i++) {
|
|
||||||
nsCOMPtr<nsIIOActivityData> data = new IOActivityData(mActivities[i]);
|
|
||||||
nsresult rv = array->AppendElement(data);
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
||||||
return rv;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
obs->NotifyObservers(array, NS_IO_ACTIVITY, nullptr);
|
|
||||||
return NS_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
explicit NotifyIOActivity()
|
|
||||||
: mozilla::Runnable("NotifyIOActivity")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
FallibleTArray<IOActivity> mActivities;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Class IOActivityMonitor
|
// Class IOActivityMonitor
|
||||||
//
|
//
|
||||||
NS_IMPL_ISUPPORTS(IOActivityMonitor, nsITimerCallback, nsINamed)
|
NS_IMPL_ISUPPORTS(IOActivityMonitor, nsINamed)
|
||||||
|
|
||||||
IOActivityMonitor::IOActivityMonitor()
|
IOActivityMonitor::IOActivityMonitor()
|
||||||
: mInterval(PR_INTERVAL_NO_TIMEOUT)
|
: mLock("IOActivityMonitor::mLock")
|
||||||
, mLock("IOActivityMonitor::mLock")
|
|
||||||
{
|
{
|
||||||
RefPtr<IOActivityMonitor> mon(gInstance);
|
RefPtr<IOActivityMonitor> mon(gInstance);
|
||||||
MOZ_ASSERT(!mon, "multiple IOActivityMonitor instances!");
|
MOZ_ASSERT(!mon, "multiple IOActivityMonitor instances!");
|
||||||
}
|
}
|
||||||
|
|
||||||
NS_IMETHODIMP
|
|
||||||
IOActivityMonitor::Notify(nsITimer* aTimer)
|
|
||||||
{
|
|
||||||
return NotifyActivities();
|
|
||||||
}
|
|
||||||
|
|
||||||
// static
|
// static
|
||||||
nsresult
|
void
|
||||||
IOActivityMonitor::NotifyActivities()
|
IOActivityMonitor::RequestActivities(dom::Promise* aPromise)
|
||||||
{
|
{
|
||||||
|
MOZ_ASSERT(aPromise);
|
||||||
RefPtr<IOActivityMonitor> mon(gInstance);
|
RefPtr<IOActivityMonitor> mon(gInstance);
|
||||||
if (!IsActive()) {
|
if (!IsActive()) {
|
||||||
return NS_ERROR_FAILURE;
|
aPromise->MaybeReject(NS_ERROR_FAILURE);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
return mon->NotifyActivities_Internal();
|
mon->RequestActivitiesInternal(aPromise);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
void
|
||||||
IOActivityMonitor::NotifyActivities_Internal()
|
IOActivityMonitor::RequestActivitiesInternal(dom::Promise* aPromise)
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
nsresult result = NS_OK;
|
||||||
nsCOMPtr<nsIRunnable> ev = NotifyIOActivity::Create(mActivities, lock);
|
FallibleTArray<dom::IOActivityDataDictionary> activities;
|
||||||
nsresult rv = SystemGroup::EventTargetFor(TaskCategory::Performance)->Dispatch(ev.forget());
|
|
||||||
if (NS_FAILED(rv)) {
|
{
|
||||||
NS_WARNING("NS_DispatchToMainThread failed");
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
return rv;
|
// Remove inactive activities
|
||||||
}
|
for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
|
||||||
// Reset the counters, remove inactive activities
|
dom::IOActivityDataDictionary* activity = &iter.Data();
|
||||||
for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
|
if (activity->mRx == 0 && activity->mTx == 0) {
|
||||||
IOActivity* activity = iter.Data();
|
iter.Remove();
|
||||||
if (activity->Inactive()) {
|
} else {
|
||||||
iter.Remove();
|
if (NS_WARN_IF(!activities.AppendElement(iter.Data(), fallible))) {
|
||||||
} else {
|
result = NS_ERROR_OUT_OF_MEMORY;
|
||||||
activity->Reset();
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NS_OK;
|
|
||||||
|
if (NS_WARN_IF(NS_FAILED(result))) {
|
||||||
|
aPromise->MaybeReject(result);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
aPromise->MaybeResolve(activities);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
|
@ -434,13 +348,13 @@ IOActivityMonitor::IsActive()
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IOActivityMonitor::Init(int32_t aInterval)
|
IOActivityMonitor::Init()
|
||||||
{
|
{
|
||||||
if (IsActive()) {
|
if (IsActive()) {
|
||||||
return NS_ERROR_ALREADY_INITIALIZED;
|
return NS_ERROR_ALREADY_INITIALIZED;
|
||||||
}
|
}
|
||||||
RefPtr<IOActivityMonitor> mon = new IOActivityMonitor();
|
RefPtr<IOActivityMonitor> mon = new IOActivityMonitor();
|
||||||
nsresult rv = mon->Init_Internal(aInterval);
|
nsresult rv = mon->InitInternal();
|
||||||
if (NS_SUCCEEDED(rv)) {
|
if (NS_SUCCEEDED(rv)) {
|
||||||
gInstance = mon;
|
gInstance = mon;
|
||||||
}
|
}
|
||||||
|
|
@ -448,7 +362,7 @@ IOActivityMonitor::Init(int32_t aInterval)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IOActivityMonitor::Init_Internal(int32_t aInterval)
|
IOActivityMonitor::InitInternal()
|
||||||
{
|
{
|
||||||
// wraps the socket APIs
|
// wraps the socket APIs
|
||||||
if (!sNetActivityMonitorLayerMethodsPtr) {
|
if (!sNetActivityMonitorLayerMethodsPtr) {
|
||||||
|
|
@ -468,20 +382,7 @@ IOActivityMonitor::Init_Internal(int32_t aInterval)
|
||||||
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
|
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
|
||||||
}
|
}
|
||||||
|
|
||||||
mInterval = aInterval;
|
return NS_OK;
|
||||||
|
|
||||||
// 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;
|
|
||||||
}
|
|
||||||
return mTimer->InitWithCallback(this, mInterval, nsITimer::TYPE_REPEATING_SLACK);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
|
@ -491,16 +392,13 @@ IOActivityMonitor::Shutdown()
|
||||||
if (!mon) {
|
if (!mon) {
|
||||||
return NS_ERROR_NOT_INITIALIZED;
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
}
|
}
|
||||||
return mon->Shutdown_Internal();
|
return mon->ShutdownInternal();
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IOActivityMonitor::Shutdown_Internal()
|
IOActivityMonitor::ShutdownInternal()
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
if (mTimer) {
|
|
||||||
mTimer->Cancel();
|
|
||||||
}
|
|
||||||
mActivities.Clear();
|
mActivities.Clear();
|
||||||
gInstance = nullptr;
|
gInstance = nullptr;
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
|
|
@ -561,13 +459,15 @@ IOActivityMonitor::MonitorFile(PRFileDesc *aFd, const char* aPath)
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
IOActivity*
|
bool
|
||||||
IOActivityMonitor::GetActivity(const nsACString& aLocation)
|
IOActivityMonitor::IncrementActivity(const nsACString& aLocation, uint32_t aRx, uint32_t aTx)
|
||||||
{
|
{
|
||||||
mLock.AssertCurrentThreadOwns();
|
mLock.AssertCurrentThreadOwns();
|
||||||
if (auto entry = mActivities.Lookup(aLocation)) {
|
if (auto entry = mActivities.Lookup(aLocation)) {
|
||||||
// already registered
|
// already registered
|
||||||
return entry.Data();
|
entry.Data().mTx += aTx;
|
||||||
|
entry.Data().mRx += aRx;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
// Creating a new IOActivity. Notice that mActivities will
|
// Creating a new IOActivity. Notice that mActivities will
|
||||||
// grow indefinitely, which is OK since we won't have
|
// grow indefinitely, which is OK since we won't have
|
||||||
|
|
@ -575,13 +475,15 @@ IOActivityMonitor::GetActivity(const nsACString& aLocation)
|
||||||
// want to assert we have at the most 1000 entries
|
// want to assert we have at the most 1000 entries
|
||||||
MOZ_ASSERT(mActivities.Count() < MAX_ACTIVITY_ENTRIES);
|
MOZ_ASSERT(mActivities.Count() < MAX_ACTIVITY_ENTRIES);
|
||||||
|
|
||||||
// Entries are removed in the timer when they are inactive.
|
dom::IOActivityDataDictionary activity;
|
||||||
IOActivity* activity = new IOActivity(aLocation);
|
activity.mLocation.Assign(aLocation);
|
||||||
|
activity.mTx = aTx;
|
||||||
|
activity.mRx = aRx;
|
||||||
|
|
||||||
if (NS_WARN_IF(!mActivities.Put(aLocation, activity, fallible))) {
|
if (NS_WARN_IF(!mActivities.Put(aLocation, activity, fallible))) {
|
||||||
delete activity;
|
return false;
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
return activity;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
|
@ -591,7 +493,7 @@ IOActivityMonitor::Write(const nsACString& aLocation, uint32_t aAmount)
|
||||||
if (!mon) {
|
if (!mon) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
return mon->Write_Internal(aLocation, aAmount);
|
return mon->WriteInternal(aLocation, aAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
|
|
@ -605,14 +507,12 @@ IOActivityMonitor::Write(PRFileDesc *fd, uint32_t aAmount)
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IOActivityMonitor::Write_Internal(const nsACString& aLocation, uint32_t aAmount)
|
IOActivityMonitor::WriteInternal(const nsACString& aLocation, uint32_t aAmount)
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
IOActivity* activity = GetActivity(aLocation);
|
if (!IncrementActivity(aLocation, aAmount, 0)) {
|
||||||
if (!activity) {
|
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
activity->tx += aAmount;
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -633,17 +533,15 @@ IOActivityMonitor::Read(const nsACString& aLocation, uint32_t aAmount)
|
||||||
if (!mon) {
|
if (!mon) {
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
return mon->Read_Internal(aLocation, aAmount);
|
return mon->ReadInternal(aLocation, aAmount);
|
||||||
}
|
}
|
||||||
|
|
||||||
nsresult
|
nsresult
|
||||||
IOActivityMonitor::Read_Internal(const nsACString& aLocation, uint32_t aAmount)
|
IOActivityMonitor::ReadInternal(const nsACString& aLocation, uint32_t aAmount)
|
||||||
{
|
{
|
||||||
mozilla::MutexAutoLock lock(mLock);
|
mozilla::MutexAutoLock lock(mLock);
|
||||||
IOActivity* activity = GetActivity(aLocation);
|
if (!IncrementActivity(aLocation, 0, aAmount)) {
|
||||||
if (!activity) {
|
|
||||||
return NS_ERROR_FAILURE;
|
return NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
activity->rx += aAmount;
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,88 +7,45 @@
|
||||||
#ifndef IOActivityMonitor_h___
|
#ifndef IOActivityMonitor_h___
|
||||||
#define IOActivityMonitor_h___
|
#define IOActivityMonitor_h___
|
||||||
|
|
||||||
|
#include "mozilla/dom/ChromeUtilsBinding.h"
|
||||||
#include "nsCOMPtr.h"
|
#include "nsCOMPtr.h"
|
||||||
#include "nscore.h"
|
#include "nscore.h"
|
||||||
#include "nsClassHashtable.h"
|
#include "nsClassHashtable.h"
|
||||||
#include "nsDataHashtable.h"
|
#include "nsDataHashtable.h"
|
||||||
#include "nsHashKeys.h"
|
#include "nsHashKeys.h"
|
||||||
#include "nsIIOActivityData.h"
|
|
||||||
#include "nsISupports.h"
|
#include "nsISupports.h"
|
||||||
#include "nsITimer.h"
|
|
||||||
#include "prinrval.h"
|
#include "prinrval.h"
|
||||||
#include "prio.h"
|
#include "prio.h"
|
||||||
#include "private/pprio.h"
|
#include "private/pprio.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
|
||||||
namespace mozilla { namespace net {
|
namespace mozilla {
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
class Promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace net {
|
||||||
|
|
||||||
#define IO_ACTIVITY_ENABLED_PREF "io.activity.enabled"
|
#define IO_ACTIVITY_ENABLED_PREF "io.activity.enabled"
|
||||||
#define IO_ACTIVITY_INTERVAL_PREF "io.activity.intervalMilliseconds"
|
|
||||||
|
|
||||||
//
|
typedef nsDataHashtable<nsCStringHashKey, dom::IOActivityDataDictionary> Activities;
|
||||||
// IOActivity keeps track of the amount of data
|
|
||||||
// sent and received for an FD / Location
|
|
||||||
//
|
|
||||||
struct IOActivity {
|
|
||||||
// the resource location, can be:
|
|
||||||
// - socket://ip:port
|
|
||||||
// - file://absolute/path
|
|
||||||
nsCString location;
|
|
||||||
|
|
||||||
// bytes received/read (rx) and sent/written (tx)
|
|
||||||
uint32_t rx;
|
|
||||||
uint32_t tx;
|
|
||||||
|
|
||||||
explicit IOActivity(const nsACString& aLocation) {
|
|
||||||
location.Assign(aLocation);
|
|
||||||
rx = 0;
|
|
||||||
tx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns true if no data was transferred
|
|
||||||
bool Inactive() {
|
|
||||||
return rx == 0 && tx == 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the data to zero
|
|
||||||
void Reset() {
|
|
||||||
rx = 0;
|
|
||||||
tx = 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef nsClassHashtable<nsCStringHashKey, IOActivity> Activities;
|
|
||||||
|
|
||||||
// XPCOM Wrapper for an IOActivity
|
|
||||||
class IOActivityData final : public nsIIOActivityData
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
NS_DECL_ISUPPORTS
|
|
||||||
NS_DECL_NSIIOACTIVITYDATA
|
|
||||||
explicit IOActivityData(IOActivity aActivity)
|
|
||||||
: mActivity(aActivity) {}
|
|
||||||
private:
|
|
||||||
~IOActivityData() = default;
|
|
||||||
IOActivity mActivity;
|
|
||||||
};
|
|
||||||
|
|
||||||
// IOActivityMonitor has several roles:
|
// IOActivityMonitor has several roles:
|
||||||
// - maintains an IOActivity per resource and updates it
|
// - maintains an IOActivity per resource and updates it
|
||||||
// - sends a dump of the activities to observers that wants
|
// - sends a dump of the activities to a promise via RequestActivities
|
||||||
// to get that info, via a timer
|
|
||||||
class IOActivityMonitor final
|
class IOActivityMonitor final
|
||||||
: public nsITimerCallback
|
: public nsINamed
|
||||||
, public nsINamed
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IOActivityMonitor();
|
IOActivityMonitor();
|
||||||
|
|
||||||
NS_DECL_THREADSAFE_ISUPPORTS
|
NS_DECL_THREADSAFE_ISUPPORTS
|
||||||
NS_DECL_NSITIMERCALLBACK
|
|
||||||
NS_DECL_NSINAMED
|
NS_DECL_NSINAMED
|
||||||
|
|
||||||
// initializes and destroys the singleton
|
// initializes and destroys the singleton
|
||||||
static nsresult Init(int32_t aInterval);
|
static nsresult Init();
|
||||||
static nsresult Shutdown();
|
static nsresult Shutdown();
|
||||||
|
|
||||||
// collect amounts of data that are written/read by location
|
// collect amounts of data that are written/read by location
|
||||||
|
|
@ -101,25 +58,17 @@ public:
|
||||||
static nsresult Write(PRFileDesc *fd, uint32_t aAmount);
|
static nsresult Write(PRFileDesc *fd, uint32_t aAmount);
|
||||||
|
|
||||||
static bool IsActive();
|
static bool IsActive();
|
||||||
|
static void RequestActivities(dom::Promise* aPromise);
|
||||||
// collects activities and notifies observers
|
|
||||||
// this method can be called manually or via the timer callback
|
|
||||||
static nsresult NotifyActivities();
|
|
||||||
private:
|
private:
|
||||||
virtual ~IOActivityMonitor() = default;
|
~IOActivityMonitor() = default;
|
||||||
nsresult Init_Internal(int32_t aInterval);
|
nsresult InitInternal();
|
||||||
nsresult Shutdown_Internal();
|
nsresult ShutdownInternal();
|
||||||
|
bool IncrementActivity(const nsACString& location, uint32_t aRx, uint32_t aTx);
|
||||||
IOActivity* GetActivity(const nsACString& location);
|
nsresult WriteInternal(const nsACString& location, uint32_t aAmount);
|
||||||
nsresult Write_Internal(const nsACString& location, uint32_t aAmount);
|
nsresult ReadInternal(const nsACString& location, uint32_t aAmount);
|
||||||
nsresult Read_Internal(const nsACString& location, uint32_t aAmount);
|
void RequestActivitiesInternal(dom::Promise* aPromise);
|
||||||
nsresult NotifyActivities_Internal();
|
|
||||||
|
|
||||||
Activities mActivities;
|
Activities mActivities;
|
||||||
|
|
||||||
// timer used to send notifications
|
|
||||||
uint32_t mInterval;
|
|
||||||
nsCOMPtr<nsITimer> mTimer;
|
|
||||||
// protects mActivities accesses
|
// protects mActivities accesses
|
||||||
Mutex mLock;
|
Mutex mLock;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -52,7 +52,6 @@ XPIDL_SOURCES += [
|
||||||
'nsIIncrementalStreamLoader.idl',
|
'nsIIncrementalStreamLoader.idl',
|
||||||
'nsIInputStreamChannel.idl',
|
'nsIInputStreamChannel.idl',
|
||||||
'nsIInputStreamPump.idl',
|
'nsIInputStreamPump.idl',
|
||||||
'nsIIOActivityData.idl',
|
|
||||||
'nsIIOService.idl',
|
'nsIIOService.idl',
|
||||||
'nsILoadContextInfo.idl',
|
'nsILoadContextInfo.idl',
|
||||||
'nsILoadGroup.idl',
|
'nsILoadGroup.idl',
|
||||||
|
|
|
||||||
|
|
@ -1,19 +0,0 @@
|
||||||
/* 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"
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Keep tracks of the bytes that are sent (tx) and received (rx)
|
|
||||||
* into a socket identified by its file descriptor (fd)
|
|
||||||
* for a given host & port.
|
|
||||||
*/
|
|
||||||
[scriptable, builtinclass, uuid(30d5f743-939e-46c6-808a-7ea07c77028e)]
|
|
||||||
interface nsIIOActivityData : nsISupports
|
|
||||||
{
|
|
||||||
readonly attribute AUTF8String location;
|
|
||||||
readonly attribute long rx;
|
|
||||||
readonly attribute long tx;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
@ -1448,7 +1448,7 @@ nsSocketTransportService::Observe(nsISupports *subject,
|
||||||
if (!Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false)) {
|
if (!Preferences::GetBool(IO_ACTIVITY_ENABLED_PREF, false)) {
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
return net::IOActivityMonitor::Init(Preferences::GetInt(IO_ACTIVITY_INTERVAL_PREF, 0));
|
return net::IOActivityMonitor::Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!strcmp(topic, "last-pb-context-exited")) {
|
if (!strcmp(topic, "last-pb-context-exited")) {
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
[DEFAULT]
|
[DEFAULT]
|
||||||
support-files =
|
support-files =
|
||||||
dummy.html
|
dummy.html
|
||||||
|
ioactivity.html
|
||||||
|
|
||||||
[browser_about_cache.js]
|
[browser_about_cache.js]
|
||||||
[browser_NetUtil.js]
|
[browser_NetUtil.js]
|
||||||
|
|
|
||||||
|
|
@ -4,80 +4,45 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
const TEST_URL = "http://example.com/browser/dom/tests/browser/dummy.html";
|
const ROOT_URL = getRootDirectory(gTestPath).replace("chrome://mochitests/content/",
|
||||||
|
"https://example.com/");
|
||||||
|
const TEST_URL = "about:license";
|
||||||
|
const TEST_URL2 = ROOT_URL + "ioactivity.html";
|
||||||
|
|
||||||
var gotSocket = false;
|
var gotSocket = false;
|
||||||
var gotFile = false;
|
var gotFile = false;
|
||||||
var gotSqlite = false;
|
var gotSqlite = false;
|
||||||
var gotEmptyData = false;
|
var gotEmptyData = false;
|
||||||
var networkActivity = function(subject, topic, value) {
|
|
||||||
subject.QueryInterface(Ci.nsIMutableArray);
|
function processResults(results) {
|
||||||
let enumerator = subject.enumerate();
|
for (let data of results) {
|
||||||
while (enumerator.hasMoreElements()) {
|
console.log(data.location);
|
||||||
let data = enumerator.getNext();
|
|
||||||
data = data.QueryInterface(Ci.nsIIOActivityData);
|
|
||||||
gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData
|
gotEmptyData = data.rx == 0 && data.tx == 0 && !gotEmptyData
|
||||||
gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket;
|
gotSocket = data.location.startsWith("socket://127.0.0.1:") || gotSocket;
|
||||||
gotFile = data.location.endsWith(".js") || gotFile;
|
gotFile = data.location.endsWith("aboutLicense.css") || gotFile;
|
||||||
gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite;
|
gotSqlite = data.location.endsWith("places.sqlite") || gotSqlite;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
add_task(async function testRequestIOActivity() {
|
||||||
function startObserver() {
|
await SpecialPowers.pushPrefEnv({
|
||||||
gotSocket = gotFile = gotSqlite = gotEmptyData = false;
|
"set": [
|
||||||
Services.obs.addObserver(networkActivity, "io-activity");
|
["io.activity.enabled", true],
|
||||||
// why do I have to do this ??
|
]
|
||||||
|
});
|
||||||
|
waitForExplicitFinish();
|
||||||
Services.obs.notifyObservers(null, "profile-initial-state", null);
|
Services.obs.notifyObservers(null, "profile-initial-state", null);
|
||||||
}
|
|
||||||
|
|
||||||
// this test activates the timer and checks the results as they come in
|
await BrowserTestUtils.withNewTab(TEST_URL, async function(browser) {
|
||||||
add_task(async function testWithTimer() {
|
await BrowserTestUtils.withNewTab(TEST_URL2, async function(browser) {
|
||||||
await SpecialPowers.pushPrefEnv({
|
let results = await ChromeUtils.requestIOActivity();
|
||||||
"set": [
|
processResults(results);
|
||||||
["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", 500);
|
|
||||||
|
|
||||||
ok(gotSocket, "A socket was used");
|
ok(gotSocket, "A socket was used");
|
||||||
ok(gotFile, "A file was used");
|
// test deactivated for now
|
||||||
ok(gotSqlite, "A sqlite DB was used");
|
// ok(gotFile, "A file 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(gotSqlite, "A sqlite DB was used");
|
||||||
ok(!gotEmptyData, "Every I/O event had data");
|
ok(!gotEmptyData, "Every I/O event had data");
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
11
netwerk/test/browser/ioactivity.html
Normal file
11
netwerk/test/browser/ioactivity.html
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
|
||||||
|
<head>
|
||||||
|
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<p>IOActivity Test Page</p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in a new issue