# HG changeset patch # User Tarek Ziadé # Date 1528834970 25200 # Node ID 1d8ffa02b2af02b23a34f426466a8f26395ac0d2 # Parent ae437b73631a30697f8b4655df29e5d410ac231a Bug 1447931 - NetworkActivity becomes IOActivity - r=baku,mak,valentin Generalizes NetworkActivity so it can be used for sockets but also disk files. The host/port data becomes a single location string prefixed with socket:// or file:// and we're not using the FD as the identifier anymore. IOActivityMonitor is now used in three places: - nsFileStreams for plain files - TelemetryVFS for sqlite files - nsSocketTransport & nsUDPSocket for UDP & TCP sockets MozReview-Commit-ID: GNu5o400PaV diff --git a/dom/tests/browser/dummy.html b/dom/tests/browser/dummy.html --- a/dom/tests/browser/dummy.html +++ b/dom/tests/browser/dummy.html @@ -1,9 +1,13 @@ + Dummy test page

Dummy test page

+ diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -5367,21 +5367,23 @@ pref("memory.dump_reports_on_oom", false // Number of stack frames to capture in createObjectURL for about:memory. pref("memory.blob_report.stack_frames", 0); // Disable idle observer fuzz, because only privileged content can access idle // observers (bug 780507). pref("dom.idle-observers-api.fuzz_time.disabled", true); -// Minimum delay in milliseconds between network activity notifications (0 means -// no notifications). The delay is the same for both download and upload, though +// 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. -pref("network.activity.intervalMilliseconds", 0); +pref("io.activity.intervalMilliseconds", 0); // If true, reuse the same global for (almost) everything loaded by the component // loader (JS components, JSMs, etc). This saves memory, but makes it possible // for the scripts to interfere with each other. A restart is required for this // to take effect. pref("jsloader.shareGlobal", false); // When we're asked to take a screenshot, don't wait more than 2000ms for the diff --git a/netwerk/base/NetworkActivityMonitor.cpp b/netwerk/base/IOActivityMonitor.cpp rename from netwerk/base/NetworkActivityMonitor.cpp rename to netwerk/base/IOActivityMonitor.cpp --- a/netwerk/base/NetworkActivityMonitor.cpp +++ b/netwerk/base/IOActivityMonitor.cpp @@ -1,369 +1,444 @@ /* -*- Mode: C++; tab-width: 2; 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 "NetworkActivityMonitor.h" +#include "IOActivityMonitor.h" #include "nsIObserverService.h" #include "nsPISocketTransportService.h" #include "nsPrintfCString.h" #include "nsSocketTransport2.h" #include "nsSocketTransportService2.h" #include "nsThreadUtils.h" #include "mozilla/Services.h" #include "prerror.h" #include "prio.h" #include "prmem.h" #include - using namespace mozilla::net; - -struct SocketActivity { - PROsfd fd; - uint32_t port; - nsString host; - uint32_t rx; - uint32_t tx; -}; - -mozilla::StaticRefPtr gInstance; +mozilla::StaticRefPtr gInstance; static PRDescIdentity sNetActivityMonitorLayerIdentity; static PRIOMethods sNetActivityMonitorLayerMethods; static PRIOMethods *sNetActivityMonitorLayerMethodsPtr = nullptr; +// Maximum number of activities entries in the monitoring class +#define MAX_ACTIVITY_ENTRIES 1000 + + +// ActivityMonitorSecret is stored in the activity monitor layer +// and provides a method to get the location. +// +// A location can be : +// - a TCP or UDP socket. The form will be socket://ip:port +// - a File. The form will be file://path +// +// For other cases, the location will be fd://number +class ActivityMonitorSecret final +{ +public: + // constructor used for sockets + explicit ActivityMonitorSecret(PRFileDesc* aFd) { + mFd = aFd; + mLocationSet = false; + } + + // constructor used for files + explicit ActivityMonitorSecret(PRFileDesc* aFd, const char* aLocation) { + mFd = aFd; + mLocation.AppendPrintf("file://%s", aLocation); + mLocationSet = true; + } + + nsCString getLocation() { + if (!mLocationSet) { + LazySetLocation(); + } + return mLocation; + } +private: + // Called to set the location using the FD on the first getLocation() usage + // which is typically when a socket is opened. If done earlier, at + // construction time, the host won't be bound yet. + // + // If the location is a file, it needs to be initialized in the + // constructor. + void LazySetLocation() { + mLocationSet = true; + PRFileDesc* extract = mFd; + while (PR_GetDescType(extract) == PR_DESC_LAYERED) { + if (!extract->lower) { + break; + } + extract = extract->lower; + } + + PRDescType fdType = PR_GetDescType(extract); + // we should not use LazySetLocation for files + MOZ_ASSERT(fdType != PR_DESC_FILE); + + switch (fdType) { + case PR_DESC_SOCKET_TCP: + case PR_DESC_SOCKET_UDP: { + mLocation.AppendPrintf("socket://"); + PRNetAddr addr; + PRStatus status = PR_GetSockName(mFd, &addr); + if (NS_WARN_IF(status == PR_FAILURE)) { + mLocation.AppendPrintf("unknown"); + break; + } + + // grabbing the host + char netAddr[mozilla::net::kNetAddrMaxCStrBufSize] = {0}; + status = PR_NetAddrToString(&addr, netAddr, sizeof(netAddr) - 1); + if (NS_WARN_IF(status == PR_FAILURE) || netAddr[0] == 0) { + mLocation.AppendPrintf("unknown"); + break; + } + mLocation.Append(netAddr); + + // adding the port + uint16_t port; + if (addr.raw.family == PR_AF_INET) { + port = addr.inet.port; + } else { + port = addr.ipv6.port; + } + mLocation.AppendPrintf(":%d", port); + } break; + + // for all other cases, we just send back fd:// + default: { + mLocation.AppendPrintf("fd://%d", PR_FileDesc2NativeHandle(mFd)); + } + } // end switch + } +private: + nsCString mLocation; + bool mLocationSet; + PRFileDesc* mFd; +}; + +// FileDesc2Location converts a PRFileDesc into a "location" by +// grabbing the ActivityMonitorSecret in layer->secret +static nsAutoCString +FileDesc2Location(PRFileDesc *fd) +{ + nsAutoCString location; + PRFileDesc *monitorLayer = PR_GetIdentitiesLayer(fd, sNetActivityMonitorLayerIdentity); + if (!monitorLayer) { + location.AppendPrintf("unknown"); + return location; + } + + ActivityMonitorSecret* secret = (ActivityMonitorSecret*)monitorLayer->secret; + location.AppendPrintf("%s", secret->getLocation().get()); + return location; +} + +// +// Wrappers around the socket APIS +// static PRStatus nsNetMon_Connect(PRFileDesc *fd, const PRNetAddr *addr, PRIntervalTime timeout) { - PRStatus ret; - PRErrorCode code; - ret = fd->lower->methods->connect(fd->lower, addr, timeout); - if (ret == PR_SUCCESS || (code = PR_GetError()) == PR_WOULD_BLOCK_ERROR || - code == PR_IN_PROGRESS_ERROR) { - NetworkActivityMonitor::RegisterFd(fd, addr); - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, 0); - } - return ret; + return fd->lower->methods->connect(fd->lower, addr, timeout); } static PRStatus nsNetMon_Close(PRFileDesc *fd) { if (!fd) { return PR_FAILURE; } - NetworkActivityMonitor::UnregisterFd(fd); PRFileDesc* layer = PR_PopIOLayer(fd, PR_TOP_IO_LAYER); MOZ_RELEASE_ASSERT(layer && layer->identity == sNetActivityMonitorLayerIdentity, "NetActivityMonitor Layer not on top of stack"); + + if (layer->secret) { + delete (ActivityMonitorSecret *)layer->secret; + layer->secret = nullptr; + } layer->dtor(layer); return fd->methods->close(fd); } static int32_t nsNetMon_Read(PRFileDesc *fd, void *buf, int32_t len) { - int32_t ret; - ret = fd->lower->methods->read(fd->lower, buf, len); + int32_t ret = fd->lower->methods->read(fd->lower, buf, len); if (ret >= 0) { - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload, fd, len); + IOActivityMonitor::Read(fd, len); } return ret; } static int32_t nsNetMon_Write(PRFileDesc *fd, const void *buf, int32_t len) { - int32_t ret; - ret = fd->lower->methods->write(fd->lower, buf, len); + int32_t ret = fd->lower->methods->write(fd->lower, buf, len); if (ret > 0) { - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, len); + IOActivityMonitor::Write(fd, len); } return ret; } static int32_t nsNetMon_Writev(PRFileDesc *fd, const PRIOVec *iov, int32_t size, PRIntervalTime timeout) { - int32_t ret; - ret = fd->lower->methods->writev(fd->lower, iov, size, timeout); + int32_t ret = fd->lower->methods->writev(fd->lower, iov, size, timeout); if (ret > 0) { - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, size); + IOActivityMonitor::Write(fd, size); } return ret; } static int32_t nsNetMon_Recv(PRFileDesc *fd, void *buf, int32_t amount, int flags, PRIntervalTime timeout) { - int32_t ret; - ret = fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout); - if (ret >= 0) { - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload, fd, amount); + int32_t ret = fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout); + if (ret > 0) { + IOActivityMonitor::Read(fd, amount); } return ret; } static int32_t nsNetMon_Send(PRFileDesc *fd, const void *buf, int32_t amount, int flags, PRIntervalTime timeout) { - int32_t ret; - ret = fd->lower->methods->send(fd->lower, buf, amount, flags, timeout); + int32_t ret = fd->lower->methods->send(fd->lower, buf, amount, flags, timeout); if (ret > 0) { - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, amount); + IOActivityMonitor::Write(fd, amount); } return ret; } static int32_t nsNetMon_RecvFrom(PRFileDesc *fd, void *buf, int32_t amount, int flags, PRNetAddr *addr, PRIntervalTime timeout) { - int32_t ret; - ret = fd->lower->methods->recvfrom(fd->lower, - buf, - amount, - flags, - addr, - timeout); - if (ret >= 0) { - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload, fd, amount); + int32_t ret = fd->lower->methods->recvfrom(fd->lower, buf, amount, flags, + addr, timeout); + if (ret > 0) { + IOActivityMonitor::Read(fd, amount); } return ret; } static int32_t nsNetMon_SendTo(PRFileDesc *fd, const void *buf, int32_t amount, int flags, const PRNetAddr *addr, PRIntervalTime timeout) { - int32_t ret; - ret = fd->lower->methods->sendto(fd->lower, - buf, - amount, - flags, - addr, - timeout); + int32_t ret = fd->lower->methods->sendto(fd->lower, buf, amount, flags, + addr, timeout); if (ret > 0) { - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kUpload, fd, amount); + IOActivityMonitor::Write(fd, amount); } return ret; } static int32_t nsNetMon_AcceptRead(PRFileDesc *listenSock, PRFileDesc **acceptedSock, PRNetAddr **peerAddr, void *buf, int32_t amount, PRIntervalTime timeout) { - int32_t ret; - ret = listenSock->lower->methods->acceptread(listenSock->lower, - acceptedSock, - peerAddr, - buf, - amount, - timeout); + int32_t ret = listenSock->lower->methods->acceptread(listenSock->lower, acceptedSock, + peerAddr, buf, amount, timeout); if (ret > 0) { - NetworkActivityMonitor::DataInOut(NetworkActivityMonitor::kDownload, listenSock, amount); + IOActivityMonitor::Read(listenSock, amount); } return ret; } -NS_IMPL_ISUPPORTS(NetworkData, nsINetworkActivityData); +// +// Class IOActivityData +// +NS_IMPL_ISUPPORTS(IOActivityData, nsIIOActivityData); NS_IMETHODIMP -NetworkData::GetHost(nsAString& aHost) { - aHost = mHost; +IOActivityData::GetLocation(nsACString& aLocation) { + aLocation = mActivity.location; return NS_OK; }; NS_IMETHODIMP -NetworkData::GetPort(int32_t* aPort) { - *aPort = mPort; - return NS_OK; -}; - -NS_IMETHODIMP -NetworkData::GetRx(int32_t* aRx) { - *aRx = mRx; - return NS_OK; -}; - -NS_IMETHODIMP -NetworkData::GetTx(int32_t* aTx) { - *aTx = mTx; +IOActivityData::GetRx(int32_t* aRx) { + *aRx = mActivity.rx; return NS_OK; }; NS_IMETHODIMP -NetworkData::GetFd(int32_t* aFd) { - *aFd = mFd; +IOActivityData::GetTx(int32_t* aTx) { + *aTx = mActivity.tx; return NS_OK; }; -class NotifyNetworkActivity : public mozilla::Runnable { +// +// 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: - explicit NotifyNetworkActivity(NetworkActivity* aActivity) - : mozilla::Runnable("NotifyNetworkActivity") - + static already_AddRefed + Create(Activities& aActivities, const mozilla::MutexAutoLock& aProofOfLock) { - uint32_t rx; - uint32_t tx; - PROsfd fd; - for (auto iter = aActivity->rx.Iter(); !iter.Done(); iter.Next()) { - rx = iter.Data(); - fd = iter.Key(); - tx = aActivity->tx.Get(fd); - if (rx == 0 && tx == 0) { - // nothing to do - } else { - SocketActivity activity; - activity.fd = fd; - activity.rx = rx; - activity.tx = tx; - activity.host = aActivity->host.Get(fd); - activity.port = aActivity->port.Get(fd); - mActivities.AppendElement(activity); + RefPtr 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 result(runnable); + return result.forget(); } NS_IMETHODIMP Run() override { MOZ_ASSERT(NS_IsMainThread()); - if (mActivities.Length() == 0) { return NS_OK; } nsCOMPtr obs = mozilla::services::GetObserverService(); if (!obs) { return NS_ERROR_FAILURE; } nsCOMPtr 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 data = - new NetworkData(mActivities[i].host, - mActivities[i].port, - mActivities[i].fd, - mActivities[i].rx, - mActivities[i].tx); - + nsCOMPtr data = new IOActivityData(mActivities[i]); nsresult rv = array->AppendElement(data); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } } - obs->NotifyObservers(array, NS_NETWORK_ACTIVITY, nullptr); + obs->NotifyObservers(array, NS_IO_ACTIVITY, nullptr); return NS_OK; } + private: - nsTArray mActivities; + explicit NotifyIOActivity() + : mozilla::Runnable("NotifyIOActivity") + { + } + + FallibleTArray mActivities; }; -NS_IMPL_ISUPPORTS(NetworkActivityMonitor, nsITimerCallback, nsINamed) +// +// Class IOActivityMonitor +// +NS_IMPL_ISUPPORTS(IOActivityMonitor, nsITimerCallback, nsINamed) -NetworkActivityMonitor::NetworkActivityMonitor() +IOActivityMonitor::IOActivityMonitor() : mInterval(PR_INTERVAL_NO_TIMEOUT) - , mLock("NetworkActivityMonitor::mLock") + , mLock("IOActivityMonitor::mLock") { - RefPtr mon(gInstance); - MOZ_ASSERT(!mon, "multiple NetworkActivityMonitor instances!"); + RefPtr mon(gInstance); + MOZ_ASSERT(!mon, "multiple IOActivityMonitor instances!"); } NS_IMETHODIMP -NetworkActivityMonitor::Notify(nsITimer* aTimer) +IOActivityMonitor::Notify(nsITimer* aTimer) { mozilla::MutexAutoLock lock(mLock); - nsCOMPtr ev = new NotifyNetworkActivity(&mActivity); - NS_DispatchToMainThread(ev); - // reset the counters - for (auto iter = mActivity.host.Iter(); !iter.Done(); iter.Next()) { - uint32_t fd = iter.Key(); - mActivity.tx.Put(fd, 0); - mActivity.rx.Put(fd, 0); + nsCOMPtr ev = NotifyIOActivity::Create(mActivities, lock); + nsresult rv = SystemGroup::EventTargetFor(TaskCategory::Performance)->Dispatch(ev.forget()); + if (NS_FAILED(rv)) { + NS_WARNING("NS_DispatchToMainThread failed"); + return rv; + } + // Reset the counters, remove inactive activities + for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) { + IOActivity* activity = iter.Data(); + if (activity->Inactive()) { + iter.Remove(); + } else { + activity->Reset(); + } } return NS_OK; } +// static NS_IMETHODIMP -NetworkActivityMonitor::GetName(nsACString& aName) +IOActivityMonitor::GetName(nsACString& aName) { - aName.AssignLiteral("NetworkActivityMonitor"); + aName.AssignLiteral("IOActivityMonitor"); return NS_OK; } +bool +IOActivityMonitor::IsActive() +{ + return gInstance != nullptr; +} nsresult -NetworkActivityMonitor::Init(int32_t aInterval) +IOActivityMonitor::Init(int32_t aInterval) { - nsresult rv = NS_OK; - RefPtr mon(gInstance); - if (mon) { + if (IsActive()) { return NS_ERROR_ALREADY_INITIALIZED; } - mon = new NetworkActivityMonitor(); - rv = mon->Init_Internal(aInterval); + RefPtr mon = new IOActivityMonitor(); + nsresult rv = mon->Init_Internal(aInterval); if (NS_SUCCEEDED(rv)) { gInstance = mon; - } else { - rv = NS_ERROR_FAILURE; } return rv; } nsresult -NetworkActivityMonitor::Shutdown() +IOActivityMonitor::Init_Internal(int32_t aInterval) { - RefPtr mon(gInstance); - if (!mon) { - return NS_ERROR_NOT_INITIALIZED; - } - mon->Shutdown_Internal(); - gInstance = nullptr; - return NS_OK; -} - -nsresult -NetworkActivityMonitor::Init_Internal(int32_t aInterval) -{ + // wraps the socket APIs if (!sNetActivityMonitorLayerMethodsPtr) { sNetActivityMonitorLayerIdentity = PR_GetUniqueIdentity("network activity monitor layer"); sNetActivityMonitorLayerMethods = *PR_GetDefaultIOMethods(); sNetActivityMonitorLayerMethods.connect = nsNetMon_Connect; sNetActivityMonitorLayerMethods.read = nsNetMon_Read; sNetActivityMonitorLayerMethods.write = nsNetMon_Write; sNetActivityMonitorLayerMethods.writev = nsNetMon_Writev; @@ -381,133 +456,168 @@ NetworkActivityMonitor::Init_Internal(in mTimer = NS_NewTimer(); if (!mTimer) { return NS_ERROR_FAILURE; } return mTimer->InitWithCallback(this, mInterval, nsITimer::TYPE_REPEATING_SLACK); } nsresult -NetworkActivityMonitor::Shutdown_Internal() +IOActivityMonitor::Shutdown() { + RefPtr mon(gInstance); + if (!mon) { + return NS_ERROR_NOT_INITIALIZED; + } + return mon->Shutdown_Internal(); +} + +nsresult +IOActivityMonitor::Shutdown_Internal() +{ + mozilla::MutexAutoLock lock(mLock); mTimer->Cancel(); + mActivities.Clear(); + gInstance = nullptr; return NS_OK; } nsresult -NetworkActivityMonitor::AttachIOLayer(PRFileDesc *fd) +IOActivityMonitor::MonitorSocket(PRFileDesc *aFd) { - RefPtr mon(gInstance); - if (!mon) { + RefPtr mon(gInstance); + if (!IsActive()) { + return NS_OK; + } + PRFileDesc* layer; + PRStatus status; + layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity, + sNetActivityMonitorLayerMethodsPtr); + if (!layer) { + return NS_ERROR_FAILURE; + } + + ActivityMonitorSecret* secret = new ActivityMonitorSecret(aFd); + layer->secret = reinterpret_cast(secret); + status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer); + + if (status == PR_FAILURE) { + delete secret; + PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc(). + return NS_ERROR_FAILURE; + } + return NS_OK; +} + +nsresult +IOActivityMonitor::MonitorFile(PRFileDesc *aFd, const char* aPath) +{ + RefPtr mon(gInstance); + if (!IsActive()) { return NS_OK; } PRFileDesc* layer; PRStatus status; layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity, sNetActivityMonitorLayerMethodsPtr); if (!layer) { return NS_ERROR_FAILURE; } - status = PR_PushIOLayer(fd, PR_NSPR_IO_LAYER, layer); + ActivityMonitorSecret* secret = new ActivityMonitorSecret(aFd, aPath); + layer->secret = reinterpret_cast(secret); + status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer); if (status == PR_FAILURE) { + delete secret; PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc(). return NS_ERROR_FAILURE; } + + return NS_OK; +} + +IOActivity* +IOActivityMonitor::GetActivity(const nsACString& aLocation) +{ + mLock.AssertCurrentThreadOwns(); + if (auto entry = mActivities.Lookup(aLocation)) { + // already registered + return entry.Data(); + } + // Creating a new IOActivity. Notice that mActivities will + // grow indefinitely, which is OK since we won't have + // but a few hundreds entries at the most, but we + // want to assert we have at the most 1000 entries + MOZ_ASSERT(mActivities.Count() < MAX_ACTIVITY_ENTRIES); + + // Entries are removed in the timer when they are inactive. + IOActivity* activity = new IOActivity(aLocation); + if (NS_WARN_IF(!mActivities.Put(aLocation, activity, fallible))) { + delete activity; + return nullptr; + } + return activity; +} + +nsresult +IOActivityMonitor::Write(const nsACString& aLocation, uint32_t aAmount) +{ + RefPtr mon(gInstance); + if (!mon) { + return NS_ERROR_FAILURE; + } + return mon->Write_Internal(aLocation, aAmount); +} + +nsresult +IOActivityMonitor::Write(PRFileDesc *fd, uint32_t aAmount) +{ + RefPtr mon(gInstance); + if (!mon) { + return NS_ERROR_FAILURE; + } + return mon->Write(FileDesc2Location(fd), aAmount); +} + +nsresult +IOActivityMonitor::Write_Internal(const nsACString& aLocation, uint32_t aAmount) +{ + mozilla::MutexAutoLock lock(mLock); + IOActivity* activity = GetActivity(aLocation); + if (!activity) { + return NS_ERROR_FAILURE; + } + activity->tx += aAmount; return NS_OK; } nsresult -NetworkActivityMonitor::RegisterFd(PRFileDesc *aFd, const PRNetAddr *aAddr) { - MOZ_ASSERT(OnSocketThread(), "not on socket thread"); - RefPtr mon(gInstance); +IOActivityMonitor::Read(PRFileDesc *fd, uint32_t aAmount) +{ + RefPtr mon(gInstance); if (!mon) { return NS_ERROR_FAILURE; } - - PROsfd osfd = PR_FileDesc2NativeHandle(aFd); - if (NS_WARN_IF(osfd == -1)) { - return ErrorAccordingToNSPR(PR_GetError()); - } - uint16_t port; - if (aAddr->raw.family == PR_AF_INET) { - port = aAddr->inet.port; - } else { - port = aAddr->ipv6.port; - } - char _host[net::kNetAddrMaxCStrBufSize] = {0}; - nsAutoCString host; - if (PR_NetAddrToString(aAddr, _host, sizeof(_host) - 1) == PR_SUCCESS) { - host.Assign(_host); - } else { - host.AppendPrintf("N/A"); - } - return mon->RegisterFd_Internal(osfd, NS_ConvertUTF8toUTF16(host), port); -} - -nsresult -NetworkActivityMonitor::UnregisterFd(PRFileDesc *aFd) -{ - MOZ_ASSERT(OnSocketThread(), "not on socket thread"); - RefPtr mon(gInstance); - if (!mon) { - return NS_ERROR_FAILURE; - } - PROsfd osfd = PR_FileDesc2NativeHandle(aFd); - if (NS_WARN_IF(osfd == -1)) { - return ErrorAccordingToNSPR(PR_GetError()); - } - return mon->UnregisterFd_Internal(osfd); + return mon->Read(FileDesc2Location(fd), aAmount); } nsresult -NetworkActivityMonitor::UnregisterFd_Internal(PROsfd aOsfd) +IOActivityMonitor::Read(const nsACString& aLocation, uint32_t aAmount) { - mozilla::MutexAutoLock lock(mLock); - // XXX indefinitely growing list - mActivity.active.Put(aOsfd, false); - return NS_OK; -} - - -nsresult -NetworkActivityMonitor::RegisterFd_Internal(PROsfd aOsfd, const nsString& host, uint16_t port) -{ - mozilla::MutexAutoLock lock(mLock); - mActivity.port.Put(aOsfd, port); - mActivity.host.Put(aOsfd, host); - mActivity.rx.Put(aOsfd, 0); - mActivity.tx.Put(aOsfd, 0); - mActivity.active.Put(aOsfd, true); - return NS_OK; + RefPtr mon(gInstance); + if (!mon) { + return NS_ERROR_FAILURE; + } + return mon->Read_Internal(aLocation, aAmount); } nsresult -NetworkActivityMonitor::DataInOut(Direction aDirection, PRFileDesc *aFd, uint32_t aAmount) +IOActivityMonitor::Read_Internal(const nsACString& aLocation, uint32_t aAmount) { - MOZ_ASSERT(OnSocketThread(), "not on socket thread"); - RefPtr mon(gInstance); - if (!mon) { + mozilla::MutexAutoLock lock(mLock); + IOActivity* activity = GetActivity(aLocation); + if (!activity) { return NS_ERROR_FAILURE; } - PROsfd osfd = PR_FileDesc2NativeHandle(aFd); - if (NS_WARN_IF(osfd == -1)) { - return ErrorAccordingToNSPR(PR_GetError()); - } - return mon->DataInOut_Internal(osfd, aDirection, aAmount); -} - -nsresult -NetworkActivityMonitor::DataInOut_Internal(PROsfd aOsfd, Direction aDirection, uint32_t aAmount) -{ - mozilla::MutexAutoLock lock(mLock); - uint32_t current; - if (aDirection == NetworkActivityMonitor::kUpload) { - current = mActivity.tx.Get(aOsfd); - mActivity.tx.Put(aOsfd, aAmount + current); - } - else { - current = mActivity.rx.Get(aOsfd); - mActivity.rx.Put(aOsfd, aAmount + current); - } + activity->rx += aAmount; return NS_OK; } diff --git a/netwerk/base/NetworkActivityMonitor.h b/netwerk/base/IOActivityMonitor.h rename from netwerk/base/NetworkActivityMonitor.h rename to netwerk/base/IOActivityMonitor.h --- a/netwerk/base/NetworkActivityMonitor.h +++ b/netwerk/base/IOActivityMonitor.h @@ -1,96 +1,122 @@ /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- * * 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 NetworkActivityMonitor_h___ -#define NetworkActivityMonitor_h___ +#ifndef IOActivityMonitor_h___ +#define IOActivityMonitor_h___ #include "nsCOMPtr.h" #include "nscore.h" +#include "nsClassHashtable.h" #include "nsDataHashtable.h" #include "nsHashKeys.h" -#include "nsINetworkActivityData.h" +#include "nsIIOActivityData.h" #include "nsISupports.h" #include "nsITimer.h" #include "prinrval.h" #include "prio.h" #include "private/pprio.h" #include namespace mozilla { namespace net { -typedef nsDataHashtable SocketBytes; -typedef nsDataHashtable SocketPort; -typedef nsDataHashtable SocketHost; -typedef nsDataHashtable SocketActive; +// +// 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; -struct NetworkActivity { - SocketPort port; - SocketHost host; - SocketBytes rx; - SocketBytes tx; - SocketActive active; + 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 Activities; -class NetworkData final : public nsINetworkActivityData +// XPCOM Wrapper for an IOActivity +class IOActivityData final : public nsIIOActivityData { public: NS_DECL_ISUPPORTS - NS_DECL_NSINETWORKACTIVITYDATA - NetworkData(const nsString& aHost, int32_t aPort, int32_t aFd, - int32_t aRx, int32_t aTx) - : mHost(aHost), mPort(aPort), mFd(aFd), mRx(aRx), mTx(aTx) {} + NS_DECL_NSIIOACTIVITYDATA + explicit IOActivityData(IOActivity aActivity) + : mActivity(aActivity) {} private: - ~NetworkData() = default; - - nsString mHost; - int32_t mPort; - int32_t mFd; - int32_t mRx; - int32_t mTx; + ~IOActivityData() = default; + IOActivity mActivity; }; - -class NetworkActivityMonitor final +// IOActivityMonitor has several roles: +// - maintains an IOActivity per resource and updates it +// - sends a dump of the activities to observers that wants +// to get that info, via a timer +class IOActivityMonitor final : public nsITimerCallback , public nsINamed { public: - enum Direction { - kUpload = 0, - kDownload = 1 - }; - - NetworkActivityMonitor(); + IOActivityMonitor(); NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSITIMERCALLBACK NS_DECL_NSINAMED + // initializes and destroys the singleton static nsresult Init(int32_t aInterval); static nsresult Shutdown(); - static nsresult AttachIOLayer(PRFileDesc *fd); - static nsresult DataInOut(Direction aDirection, PRFileDesc *aFd, uint32_t aAmount); - static nsresult RegisterFd(PRFileDesc *aFd, const PRNetAddr *aAddr); - static nsresult UnregisterFd(PRFileDesc *aFd); + + // collect amounts of data that are written/read by location + static nsresult Read(const nsACString& location, uint32_t aAmount); + static nsresult Write(const nsACString& location, uint32_t aAmount); + + static nsresult MonitorFile(PRFileDesc *aFd, const char* aPath); + static nsresult MonitorSocket(PRFileDesc *aFd); + static nsresult Read(PRFileDesc *fd, uint32_t aAmount); + static nsresult Write(PRFileDesc *fd, uint32_t aAmount); + + static bool IsActive(); private: - virtual ~NetworkActivityMonitor() = default; + virtual ~IOActivityMonitor() = default; nsresult Init_Internal(int32_t aInterval); nsresult Shutdown_Internal(); - nsresult RegisterFd_Internal(PROsfd aOsfd, const nsString& host, uint16_t port); - nsresult UnregisterFd_Internal(PROsfd aOsfd); - nsresult DataInOut_Internal(PROsfd aOsfd, Direction aDirection, uint32_t aAmount); - uint32_t mInterval; - NetworkActivity mActivity; - nsCOMPtr mTimer; - Mutex mLock; + + IOActivity* GetActivity(const nsACString& location); + nsresult Write_Internal(const nsACString& location, uint32_t aAmount); + nsresult Read_Internal(const nsACString& location, uint32_t aAmount); + + Activities mActivities; + + // timer used to send notifications + uint32_t mInterval; + nsCOMPtr mTimer; + // protects mActivities accesses + Mutex mLock; }; } // namespace net } // namespace mozilla -#endif /* NetworkActivityMonitor_h___ */ +#endif /* IOActivityMonitor_h___ */ diff --git a/netwerk/base/moz.build b/netwerk/base/moz.build --- a/netwerk/base/moz.build +++ b/netwerk/base/moz.build @@ -47,28 +47,28 @@ XPIDL_SOURCES += [ 'nsIForcePendingChannel.idl', 'nsIFormPOSTActionChannel.idl', 'nsIHttpAuthenticatorCallback.idl', 'nsIHttpPushListener.idl', 'nsIIncrementalDownload.idl', 'nsIIncrementalStreamLoader.idl', 'nsIInputStreamChannel.idl', 'nsIInputStreamPump.idl', + 'nsIIOActivityData.idl', 'nsIIOService.idl', 'nsIIOService2.idl', 'nsILoadContextInfo.idl', 'nsILoadGroup.idl', 'nsILoadGroupChild.idl', 'nsILoadInfo.idl', 'nsIMIMEInputStream.idl', 'nsIMultiPartChannel.idl', 'nsINestedURI.idl', 'nsINetAddr.idl', 'nsINetUtil.idl', - 'nsINetworkActivityData.idl', 'nsINetworkInfoService.idl', 'nsINetworkInterceptController.idl', 'nsINetworkLinkService.idl', 'nsINetworkPredictor.idl', 'nsINetworkPredictorVerifier.idl', 'nsINetworkProperties.idl', 'nsINullChannel.idl', 'nsIParentChannel.idl', @@ -170,16 +170,17 @@ EXPORTS.mozilla += [ ] EXPORTS.mozilla.net += [ 'CaptivePortalService.h', 'ChannelDiverterChild.h', 'ChannelDiverterParent.h', 'Dashboard.h', 'DashboardTypes.h', + 'IOActivityMonitor.h', 'MemoryDownloader.h', 'MozURL.h', 'PartiallySeekableInputStream.h', 'Predictor.h', 'ReferrerPolicy.h', 'SimpleChannelParent.h', 'TCPFastOpen.h', ] @@ -187,21 +188,21 @@ EXPORTS.mozilla.net += [ UNIFIED_SOURCES += [ 'ArrayBufferInputStream.cpp', 'BackgroundFileSaver.cpp', 'CaptivePortalService.cpp', 'ChannelDiverterChild.cpp', 'ChannelDiverterParent.cpp', 'Dashboard.cpp', 'EventTokenBucket.cpp', + 'IOActivityMonitor.cpp', 'LoadContextInfo.cpp', 'LoadInfo.cpp', 'MemoryDownloader.cpp', 'MozURL.cpp', - 'NetworkActivityMonitor.cpp', 'nsAsyncRedirectVerifyHelper.cpp', 'nsAsyncStreamCopier.cpp', 'nsAuthInformationHolder.cpp', 'nsBase64Encoder.cpp', 'nsBaseChannel.cpp', 'nsBaseContentStream.cpp', 'nsBufferedStreams.cpp', 'nsChannelClassifier.cpp', diff --git a/netwerk/base/nsFileStreams.cpp b/netwerk/base/nsFileStreams.cpp --- a/netwerk/base/nsFileStreams.cpp +++ b/netwerk/base/nsFileStreams.cpp @@ -10,30 +10,36 @@ #elif defined(XP_WIN) #include #include "nsILocalFileWin.h" #else // XXX add necessary include file for ftruncate (or equivalent) #endif #include "private/pprio.h" +#include "prerror.h" +#include "IOActivityMonitor.h" #include "nsFileStreams.h" #include "nsIFile.h" #include "nsReadLine.h" #include "nsIClassInfoImpl.h" +#include "nsLiteralString.h" +#include "nsSocketTransport2.h" // for ErrorAccordingToNSPR() #include "mozilla/ipc/InputStreamUtils.h" #include "mozilla/Unused.h" #include "mozilla/FileUtils.h" #include "nsNetCID.h" #include "nsXULAppAPI.h" typedef mozilla::ipc::FileDescriptor::PlatformHandleType FileHandleType; using namespace mozilla::ipc; +using namespace mozilla::net; + using mozilla::DebugOnly; using mozilla::Maybe; using mozilla::Nothing; using mozilla::Some; //////////////////////////////////////////////////////////////////////////////// // nsFileStreamBase @@ -350,17 +356,32 @@ nsFileStreamBase::DoOpen() } else #endif // XP_WIN { rv = mOpenParams.localFile->OpenNSPRFileDesc(mOpenParams.ioFlags, mOpenParams.perm, &fd); } + if (rv == NS_OK && IOActivityMonitor::IsActive()) { + auto nativePath = mOpenParams.localFile->NativePath(); + if (!nativePath.IsEmpty()) { + // registering the file to the activity monitor + #ifdef XP_WIN + // 16 bits unicode + IOActivityMonitor::MonitorFile(fd, NS_ConvertUTF16toUTF8(nativePath.get()).get()); + #else + // 8 bit unicode + IOActivityMonitor::MonitorFile(fd, nativePath.get()); + #endif + } + } + CleanUpOpen(); + if (NS_FAILED(rv)) { mState = eError; mErrorValue = rv; return rv; } mFD = fd; mState = eOpened; diff --git a/netwerk/base/nsINetworkActivityData.idl b/netwerk/base/nsIIOActivityData.idl rename from netwerk/base/nsINetworkActivityData.idl rename to netwerk/base/nsIIOActivityData.idl --- a/netwerk/base/nsINetworkActivityData.idl +++ b/netwerk/base/nsIIOActivityData.idl @@ -5,17 +5,15 @@ #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 nsINetworkActivityData : nsISupports +interface nsIIOActivityData : nsISupports { - readonly attribute DOMString host; - readonly attribute long port; + readonly attribute AUTF8String location; readonly attribute long rx; readonly attribute long tx; - readonly attribute long fd; }; diff --git a/netwerk/base/nsPISocketTransportService.idl b/netwerk/base/nsPISocketTransportService.idl --- a/netwerk/base/nsPISocketTransportService.idl +++ b/netwerk/base/nsPISocketTransportService.idl @@ -44,18 +44,18 @@ interface nsPISocketTransportService : n /** * Controls the default retransmission count for keepalive probes. */ readonly attribute long keepaliveProbeCount; }; %{C++ /* - * Network activity: we send out this topic no more than every + * I/O activity: we send out this topic no more than every * intervalMilliseconds (as set by the - * "network.activity.intervalMilliseconds" preference: if 0 no notifications - * are sent) if the network is currently active (i.e. we're sending/receiving - * data to/from the socket). + * "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). */ -#define NS_NETWORK_ACTIVITY "network-activity" +#define NS_IO_ACTIVITY "io-activity" %} diff --git a/netwerk/base/nsSocketTransport2.cpp b/netwerk/base/nsSocketTransport2.cpp --- a/netwerk/base/nsSocketTransport2.cpp +++ b/netwerk/base/nsSocketTransport2.cpp @@ -14,17 +14,17 @@ #include "nsTransportUtils.h" #include "nsProxyInfo.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsAutoPtr.h" #include "nsCOMPtr.h" #include "plstr.h" #include "prerr.h" -#include "NetworkActivityMonitor.h" +#include "IOActivityMonitor.h" #include "NSSErrorsService.h" #include "mozilla/dom/ToJSValue.h" #include "mozilla/net/NeckoChild.h" #include "nsThreadUtils.h" #include "nsISocketProviderService.h" #include "nsISocketProvider.h" #include "nsISSLSocketControl.h" #include "nsIPipe.h" @@ -1359,18 +1359,18 @@ nsSocketTransport::InitiateSocket() bool usingSSL; rv = BuildSocket(fd, proxyTransparent, usingSSL); if (NS_FAILED(rv)) { SOCKET_LOG((" BuildSocket failed [rv=%" PRIx32 "]\n", static_cast(rv))); return rv; } - // Attach network activity monitor - NetworkActivityMonitor::AttachIOLayer(fd); + // create proxy via IOActivityMonitor + IOActivityMonitor::MonitorSocket(fd); PRStatus status; // Make the socket non-blocking... PRSocketOptionData opt; opt.option = PR_SockOpt_Nonblocking; opt.value.non_blocking = true; status = PR_SetSocketOption(fd, &opt); diff --git a/netwerk/base/nsSocketTransportService2.cpp b/netwerk/base/nsSocketTransportService2.cpp --- a/netwerk/base/nsSocketTransportService2.cpp +++ b/netwerk/base/nsSocketTransportService2.cpp @@ -1,16 +1,16 @@ // vim:set sw=4 sts=4 et cin: /* 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 "nsSocketTransportService2.h" #include "nsSocketTransport2.h" -#include "NetworkActivityMonitor.h" +#include "IOActivityMonitor.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/Preferences.h" #include "nsIOService.h" #include "nsASocketHandler.h" #include "nsError.h" #include "prnetdb.h" #include "prerror.h" #include "nsIPrefService.h" @@ -43,17 +43,17 @@ static Atomic gSocke #define SEND_BUFFER_PREF "network.tcp.sendbuffer" #define KEEPALIVE_ENABLED_PREF "network.tcp.keepalive.enabled" #define KEEPALIVE_IDLE_TIME_PREF "network.tcp.keepalive.idle_time" #define KEEPALIVE_RETRY_INTERVAL_PREF "network.tcp.keepalive.retry_interval" #define KEEPALIVE_PROBE_COUNT_PREF "network.tcp.keepalive.probe_count" #define SOCKET_LIMIT_TARGET 1000U #define SOCKET_LIMIT_MIN 50U -#define INTERVAL_PREF "network.activity.intervalMilliseconds" +#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" #define MAX_TIME_FOR_PR_CLOSE_DURING_SHUTDOWN "network.sts.max_time_for_pr_close_during_shutdown" #define POLLABLE_EVENT_TIMEOUT "network.sts.pollable_event_timeout" #define REPAIR_POLLABLE_EVENT_TIME 10 @@ -695,17 +695,17 @@ nsSocketTransportService::ShutdownThread obsSvc->RemoveObserver(this, NS_NETWORK_LINK_TOPIC); } if (mAfterWakeUpTimer) { mAfterWakeUpTimer->Cancel(); mAfterWakeUpTimer = nullptr; } - NetworkActivityMonitor::Shutdown(); + IOActivityMonitor::Shutdown(); mInitialized = false; mShuttingDown = false; return NS_OK; } NS_IMETHODIMP @@ -1363,22 +1363,22 @@ nsSocketTransportService::Observe(nsISup SOCKET_LOG(("nsSocketTransportService::Observe topic=%s", topic)); if (!strcmp(topic, NS_PREFBRANCH_PREFCHANGE_TOPIC_ID)) { UpdatePrefs(); return NS_OK; } if (!strcmp(topic, "profile-initial-state")) { - int32_t interval = Preferences::GetInt(INTERVAL_PREF, 0); + int32_t interval = Preferences::GetInt(IO_ACTIVITY_INTERVAL_PREF, 0); if (interval <= 0) { return NS_OK; } - return net::NetworkActivityMonitor::Init(interval); + return net::IOActivityMonitor::Init(interval); } if (!strcmp(topic, "last-pb-context-exited")) { nsCOMPtr ev = NewRunnableMethod( "net::nsSocketTransportService::ClosePrivateConnections", this, &nsSocketTransportService::ClosePrivateConnections); nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL); diff --git a/netwerk/base/nsUDPSocket.cpp b/netwerk/base/nsUDPSocket.cpp --- a/netwerk/base/nsUDPSocket.cpp +++ b/netwerk/base/nsUDPSocket.cpp @@ -15,17 +15,17 @@ #include "nsError.h" #include "nsNetCID.h" #include "nsNetUtil.h" #include "nsIOService.h" #include "prnetdb.h" #include "prio.h" #include "nsNetAddr.h" #include "nsNetSegmentUtils.h" -#include "NetworkActivityMonitor.h" +#include "IOActivityMonitor.h" #include "nsServiceManagerUtils.h" #include "nsStreamUtils.h" #include "nsIPipe.h" #include "prerror.h" #include "nsThreadUtils.h" #include "nsIDNSRecord.h" #include "nsIDNSService.h" #include "nsICancelable.h" @@ -676,18 +676,18 @@ nsUDPSocket::InitWithAddress(const NetAd if (PR_GetSockName(mFD, &addr) != PR_SUCCESS) { NS_WARNING("cannot get socket name"); goto fail; } PRNetAddrToNetAddr(&addr, &mAddr); - // create proxy via NetworkActivityMonitor - NetworkActivityMonitor::AttachIOLayer(mFD); + // create proxy via IOActivityMonitor + IOActivityMonitor::MonitorSocket(mFD); // wait until AsyncListen is called before polling the socket for // client connections. return NS_OK; fail: Close(); return NS_ERROR_FAILURE; diff --git a/netwerk/test/browser/browser.ini b/netwerk/test/browser/browser.ini --- a/netwerk/test/browser/browser.ini +++ b/netwerk/test/browser/browser.ini @@ -5,9 +5,10 @@ support-files = [browser_about_cache.js] [browser_NetUtil.js] [browser_child_resource.js] skip-if = !crashreporter || (e10s && debug && os == "linux" && bits == 64) || debug # Bug 1370783 [browser_post_file.js] [browser_nsIFormPOSTActionChannel.js] skip-if = e10s # protocol handler and channel does not work in content process [browser_resource_navigation.js] +[browser_test_io_activity.js] [browser_cookie_sync_across_tabs.js] diff --git a/netwerk/test/browser/browser_test_io_activity.js b/netwerk/test/browser/browser_test_io_activity.js new file mode 100644 --- /dev/null +++ b/netwerk/test/browser/browser_test_io_activity.js @@ -0,0 +1,53 @@ +/* -*- 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"; + + +add_task(async function test() { + + 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; + } + }; + + Services.obs.addObserver(networkActivity, "io-activity"); + + // why do I have to do this ?? + Services.obs.notifyObservers(null, "profile-initial-state", null); + + 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); + + 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'); +}); diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -17,17 +17,16 @@ support-files = data/test_readline8.txt data/signed_win.exe socks_client_subprocess.js test_link.desktop test_link.url ../../dns/effective_tld_names.dat test_alt-data_cross_process.js -[test_network_activity.js] [test_nsIBufferedOutputStream_writeFrom_block.js] [test_cache2-00-service-get.js] [test_cache2-01-basic.js] [test_cache2-01a-basic-readonly.js] [test_cache2-01b-basic-datasize.js] [test_cache2-01c-basic-hasmeta-only.js] [test_cache2-01d-basic-not-wanted.js] [test_cache2-01e-basic-bypass-if-busy.js] diff --git a/storage/TelemetryVFS.cpp b/storage/TelemetryVFS.cpp --- a/storage/TelemetryVFS.cpp +++ b/storage/TelemetryVFS.cpp @@ -7,16 +7,17 @@ #include #include "mozilla/Telemetry.h" #include "mozilla/Preferences.h" #include "sqlite3.h" #include "nsThreadUtils.h" #include "mozilla/dom/quota/PersistenceType.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/QuotaObject.h" +#include "mozilla/net/IOActivityMonitor.h" #include "mozilla/IOInterposer.h" // The last VFS version for which this file has been updated. #define LAST_KNOWN_VFS_VERSION 3 // The last io_methods version for which this file has been updated. #define LAST_KNOWN_IOMETHODS_VERSION 3 @@ -29,16 +30,17 @@ * locking is slower than POSIX locking, so we do not want to do it by default. */ #define PREF_NFS_FILESYSTEM "storage.nfs_filesystem" namespace { using namespace mozilla; using namespace mozilla::dom::quota; +using namespace mozilla::net; struct Histograms { const char *name; const Telemetry::HistogramID readB; const Telemetry::HistogramID writeB; const Telemetry::HistogramID readMS; const Telemetry::HistogramID writeMS; const Telemetry::HistogramID syncMS; @@ -145,16 +147,19 @@ struct telemetry_file { // quota object for this file RefPtr quotaObject; // The chunk size for this file. See the documentation for // sqlite3_file_control() and FCNTL_CHUNK_SIZE. int fileChunkSize; + // The filename + char* location; + // This contains the vfs that actually does work sqlite3_file pReal[1]; }; const char* DatabasePathFromWALPath(const char *zWALName) { /** @@ -353,16 +358,17 @@ xClose(sqlite3_file *pFile) { // Scope for IOThreadAutoTimer IOThreadAutoTimer ioTimer(IOInterposeObserver::OpClose); rc = p->pReal->pMethods->xClose(p->pReal); } if( rc==SQLITE_OK ){ delete p->base.pMethods; p->base.pMethods = nullptr; p->quotaObject = nullptr; + delete[] p->location; #ifdef DEBUG p->fileChunkSize = 0; #endif } return rc; } /* @@ -370,16 +376,19 @@ xClose(sqlite3_file *pFile) */ int xRead(sqlite3_file *pFile, void *zBuf, int iAmt, sqlite_int64 iOfst) { telemetry_file *p = (telemetry_file *)pFile; IOThreadAutoTimer ioTimer(p->histograms->readMS, IOInterposeObserver::OpRead); int rc; rc = p->pReal->pMethods->xRead(p->pReal, zBuf, iAmt, iOfst); + if (rc == SQLITE_OK && IOActivityMonitor::IsActive()) { + IOActivityMonitor::Read(nsDependentCString(p->location), iAmt); + } // sqlite likes to read from empty files, this is normal, ignore it. if (rc != SQLITE_IOERR_SHORT_READ) Telemetry::Accumulate(p->histograms->readB, rc == SQLITE_OK ? iAmt : 0); return rc; } /* ** Return the current file-size of a telemetry_file. @@ -405,16 +414,20 @@ xWrite(sqlite3_file *pFile, const void * int rc; if (p->quotaObject) { MOZ_ASSERT(INT64_MAX - iOfst >= iAmt); if (!p->quotaObject->MaybeUpdateSize(iOfst + iAmt, /* aTruncate */ false)) { return SQLITE_FULL; } } rc = p->pReal->pMethods->xWrite(p->pReal, zBuf, iAmt, iOfst); + if (rc == SQLITE_OK && IOActivityMonitor::IsActive()) { + IOActivityMonitor::Write(nsDependentCString(p->location), iAmt); + } + Telemetry::Accumulate(p->histograms->writeB, rc == SQLITE_OK ? iAmt : 0); if (p->quotaObject && rc != SQLITE_OK) { NS_WARNING("xWrite failed on a quota-controlled file, attempting to " "update its current size..."); sqlite_int64 currentSize; if (xFileSize(pFile, ¤tSize) == SQLITE_OK) { p->quotaObject->MaybeUpdateSize(currentSize, /* aTruncate */ true); } @@ -656,16 +669,26 @@ xOpen(sqlite3_vfs* vfs, const char *zNam } p->histograms = h; MaybeEstablishQuotaControl(zName, p, flags); rc = orig_vfs->xOpen(orig_vfs, zName, p->pReal, flags, pOutFlags); if( rc != SQLITE_OK ) return rc; + + if (zName) { + p->location = new char[7 + strlen(zName) + 1]; + strcpy(p->location, "file://"); + strcpy(p->location + 7, zName); + } else { + p->location = new char[8]; + strcpy(p->location, "file://"); + } + if( p->pReal->pMethods ){ sqlite3_io_methods *pNew = new sqlite3_io_methods; const sqlite3_io_methods *pSub = p->pReal->pMethods; memset(pNew, 0, sizeof(*pNew)); // If the io_methods version is higher than the last known one, you should // update this VFS adding appropriate IO methods for any methods added in // the version change. pNew->iVersion = pSub->iVersion;