Bug 1447931 - NetworkActivity becomes IOActivity - r=baku,mak,valentin
authorTarek Ziadé <tarek@mozilla.com>
Tue, 12 Jun 2018 13:22:50 -0700
changeset 422376 1d8ffa02b2af02b23a34f426466a8f26395ac0d2
parent 422375 d79bd851ac958d14d4c97daa8f7b952ebaf34bdb
child 422377 0cdcd92a013fedb1f6fc80e4194a321151620a86
push id34131
push usercsabou@mozilla.com
push dateWed, 13 Jun 2018 09:21:21 +0000
treeherdermozilla-central@5182bca90d06 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, mak, valentin
bugs1447931
milestone62.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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
dom/tests/browser/dummy.html
modules/libpref/init/all.js
netwerk/base/IOActivityMonitor.cpp
netwerk/base/IOActivityMonitor.h
netwerk/base/NetworkActivityMonitor.cpp
netwerk/base/NetworkActivityMonitor.h
netwerk/base/moz.build
netwerk/base/nsFileStreams.cpp
netwerk/base/nsIIOActivityData.idl
netwerk/base/nsINetworkActivityData.idl
netwerk/base/nsPISocketTransportService.idl
netwerk/base/nsSocketTransport2.cpp
netwerk/base/nsSocketTransportService2.cpp
netwerk/base/nsUDPSocket.cpp
netwerk/test/browser/browser.ini
netwerk/test/browser/browser_test_io_activity.js
netwerk/test/unit/xpcshell.ini
storage/TelemetryVFS.cpp
--- a/dom/tests/browser/dummy.html
+++ b/dom/tests/browser/dummy.html
@@ -1,9 +1,13 @@
+<!doctype html>
 <html>
 <head>
 <title>Dummy test page</title>
 <meta http-equiv="Content-Type" content="text/html;charset=utf-8"></meta>
 </head>
 <body>
 <p>Dummy test page</p>
+<script>
+  localStorage.setItem("foo", "bar");
+</script>
 </body>
 </html>
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5117,21 +5117,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", true);
 
 // When we're asked to take a screenshot, don't wait more than 2000ms for the
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 <vector>
 
-
 using namespace mozilla::net;
 
-
-struct SocketActivity {
-    PROsfd fd;
-    uint32_t port;
-    nsString host;
-    uint32_t rx;
-    uint32_t tx;
-};
-
-mozilla::StaticRefPtr<NetworkActivityMonitor> gInstance;
+mozilla::StaticRefPtr<IOActivityMonitor> 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://<value>
+      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<nsIRunnable>
+  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<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<nsINetworkActivityData> data =
-        new NetworkData(mActivities[i].host,
-                        mActivities[i].port,
-                        mActivities[i].fd,
-                        mActivities[i].rx,
-                        mActivities[i].tx);
-
+      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_NETWORK_ACTIVITY, nullptr);
+    obs->NotifyObservers(array, NS_IO_ACTIVITY, nullptr);
     return NS_OK;
   }
+
 private:
-  nsTArray<SocketActivity> mActivities;
+  explicit NotifyIOActivity()
+    : mozilla::Runnable("NotifyIOActivity")
+  {
+  }
+
+  FallibleTArray<IOActivity> 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<NetworkActivityMonitor> mon(gInstance);
-  MOZ_ASSERT(!mon, "multiple NetworkActivityMonitor instances!");
+  RefPtr<IOActivityMonitor> mon(gInstance);
+  MOZ_ASSERT(!mon, "multiple IOActivityMonitor instances!");
 }
 
 NS_IMETHODIMP
-NetworkActivityMonitor::Notify(nsITimer* aTimer)
+IOActivityMonitor::Notify(nsITimer* aTimer)
 {
   mozilla::MutexAutoLock lock(mLock);
-  nsCOMPtr<nsIRunnable> 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<nsIRunnable> 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<NetworkActivityMonitor> mon(gInstance);
-  if (mon) {
+  if (IsActive()) {
     return NS_ERROR_ALREADY_INITIALIZED;
   }
-  mon = new NetworkActivityMonitor();
-  rv = mon->Init_Internal(aInterval);
+  RefPtr<IOActivityMonitor> 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<NetworkActivityMonitor> 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<IOActivityMonitor> 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<NetworkActivityMonitor> mon(gInstance);
-  if (!mon) {
+  RefPtr<IOActivityMonitor> 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<PRFilePrivate *>(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<IOActivityMonitor> 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<PRFilePrivate *>(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<IOActivityMonitor> mon(gInstance);
+  if (!mon) {
+    return NS_ERROR_FAILURE;
+  }
+  return mon->Write_Internal(aLocation, aAmount);
+}
+
+nsresult
+IOActivityMonitor::Write(PRFileDesc *fd, uint32_t aAmount)
+{
+  RefPtr<IOActivityMonitor> 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<NetworkActivityMonitor> mon(gInstance);
+IOActivityMonitor::Read(PRFileDesc *fd, uint32_t aAmount)
+{
+  RefPtr<IOActivityMonitor> 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<NetworkActivityMonitor> 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<IOActivityMonitor> 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<NetworkActivityMonitor> 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;
 }
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 <stdint.h>
 
 namespace mozilla { namespace net {
 
-typedef nsDataHashtable<nsUint32HashKey, uint32_t> SocketBytes;
-typedef nsDataHashtable<nsUint32HashKey, uint32_t> SocketPort;
-typedef nsDataHashtable<nsUint32HashKey, nsString> SocketHost;
-typedef nsDataHashtable<nsUint32HashKey, bool> 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<nsCStringHashKey, IOActivity> 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<nsITimer>              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<nsITimer> mTimer;
+  // protects mActivities accesses
+  Mutex mLock;
 };
 
 } // namespace net
 } // namespace mozilla
 
-#endif /* NetworkActivityMonitor_h___ */
+#endif /* IOActivityMonitor_h___ */
--- a/netwerk/base/moz.build
+++ b/netwerk/base/moz.build
@@ -47,27 +47,27 @@ XPIDL_SOURCES += [
     'nsIForcePendingChannel.idl',
     'nsIFormPOSTActionChannel.idl',
     'nsIHttpAuthenticatorCallback.idl',
     'nsIHttpPushListener.idl',
     'nsIIncrementalDownload.idl',
     'nsIIncrementalStreamLoader.idl',
     'nsIInputStreamChannel.idl',
     'nsIInputStreamPump.idl',
+    'nsIIOActivityData.idl',
     'nsIIOService.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',
@@ -167,36 +167,37 @@ EXPORTS.mozilla += [
 ]
 
 EXPORTS.mozilla.net += [
     'CaptivePortalService.h',
     'ChannelDiverterChild.h',
     'ChannelDiverterParent.h',
     'Dashboard.h',
     'DashboardTypes.h',
+    'IOActivityMonitor.h',
     'MemoryDownloader.h',
     'PartiallySeekableInputStream.h',
     'Predictor.h',
     'ReferrerPolicy.h',
     'SimpleChannelParent.h',
     'TCPFastOpen.h',
 ]
 
 UNIFIED_SOURCES += [
     'ArrayBufferInputStream.cpp',
     'BackgroundFileSaver.cpp',
     'CaptivePortalService.cpp',
     'ChannelDiverterChild.cpp',
     'ChannelDiverterParent.cpp',
     'Dashboard.cpp',
     'EventTokenBucket.cpp',
+    'IOActivityMonitor.cpp',
     'LoadContextInfo.cpp',
     'LoadInfo.cpp',
     'MemoryDownloader.cpp',
-    'NetworkActivityMonitor.cpp',
     'nsAsyncRedirectVerifyHelper.cpp',
     'nsAsyncStreamCopier.cpp',
     'nsAuthInformationHolder.cpp',
     'nsBase64Encoder.cpp',
     'nsBaseChannel.cpp',
     'nsBaseContentStream.cpp',
     'nsBufferedStreams.cpp',
     'nsChannelClassifier.cpp',
--- a/netwerk/base/nsFileStreams.cpp
+++ b/netwerk/base/nsFileStreams.cpp
@@ -10,30 +10,36 @@
 #elif defined(XP_WIN)
 #include <windows.h>
 #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;
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;
 };
 
--- 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"
 
 
 %}
--- a/netwerk/base/nsSocketTransport2.cpp
+++ b/netwerk/base/nsSocketTransport2.cpp
@@ -15,17 +15,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"
@@ -1377,18 +1377,18 @@ nsSocketTransport::InitiateSocket()
     bool usingSSL;
 
     rv = BuildSocket(fd, proxyTransparent, usingSSL);
     if (NS_FAILED(rv)) {
         SOCKET_LOG(("  BuildSocket failed [rv=%" PRIx32 "]\n", static_cast<uint32_t>(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);
--- 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"
@@ -44,17 +44,17 @@ static Atomic<PRThread*, Relaxed> 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 TELEMETRY_PREF "toolkit.telemetry.enabled"
 #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
@@ -706,17 +706,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
@@ -1441,22 +1441,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<nsIRunnable> ev = NewRunnableMethod(
         "net::nsSocketTransportService::ClosePrivateConnections",
         this,
         &nsSocketTransportService::ClosePrivateConnections);
       nsresult rv = Dispatch(ev, nsIEventTarget::DISPATCH_NORMAL);
--- a/netwerk/base/nsUDPSocket.cpp
+++ b/netwerk/base/nsUDPSocket.cpp
@@ -16,17 +16,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"
@@ -673,18 +673,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;
--- 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]
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');
+});
--- 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]
--- a/storage/TelemetryVFS.cpp
+++ b/storage/TelemetryVFS.cpp
@@ -7,16 +7,17 @@
 #include <string.h>
 #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> 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)
 {
   /**
@@ -356,16 +361,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;
 }
 
 /*
@@ -373,16 +379,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.
@@ -408,16 +417,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, &currentSize) == SQLITE_OK) {
       p->quotaObject->MaybeUpdateSize(currentSize, /* aTruncate */ true);
     }
@@ -659,16 +672,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;