forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			478 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			478 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /* -*- 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 "IOActivityMonitor.h"
 | |
| #include "nsPrintfCString.h"
 | |
| #include "nsSocketTransport2.h"
 | |
| #include "nsSocketTransportService2.h"
 | |
| #include "nsThreadUtils.h"
 | |
| #include "mozilla/dom/Promise.h"
 | |
| #include "prerror.h"
 | |
| #include "prio.h"
 | |
| #include "prmem.h"
 | |
| #include <vector>
 | |
| 
 | |
| using namespace mozilla;
 | |
| using namespace mozilla::net;
 | |
| 
 | |
| 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) {
 | |
|   return fd->lower->methods->connect(fd->lower, addr, timeout);
 | |
| }
 | |
| 
 | |
| static PRStatus nsNetMon_Close(PRFileDesc* fd) {
 | |
|   if (!fd) {
 | |
|     return PR_FAILURE;
 | |
|   }
 | |
|   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 = fd->lower->methods->read(fd->lower, buf, len);
 | |
|   if (ret >= 0) {
 | |
|     IOActivityMonitor::Read(fd, len);
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static int32_t nsNetMon_Write(PRFileDesc* fd, const void* buf, int32_t len) {
 | |
|   int32_t ret = fd->lower->methods->write(fd->lower, buf, len);
 | |
|   if (ret > 0) {
 | |
|     IOActivityMonitor::Write(fd, len);
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| static int32_t nsNetMon_Writev(PRFileDesc* fd, const PRIOVec* iov, int32_t size,
 | |
|                                PRIntervalTime timeout) {
 | |
|   int32_t ret = fd->lower->methods->writev(fd->lower, iov, size, timeout);
 | |
|   if (ret > 0) {
 | |
|     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 =
 | |
|       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 =
 | |
|       fd->lower->methods->send(fd->lower, buf, amount, flags, timeout);
 | |
|   if (ret > 0) {
 | |
|     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 = 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 =
 | |
|       fd->lower->methods->sendto(fd->lower, buf, amount, flags, addr, timeout);
 | |
|   if (ret > 0) {
 | |
|     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 = listenSock->lower->methods->acceptread(
 | |
|       listenSock->lower, acceptedSock, peerAddr, buf, amount, timeout);
 | |
|   if (ret > 0) {
 | |
|     IOActivityMonitor::Read(listenSock, amount);
 | |
|   }
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| //
 | |
| // Class IOActivityMonitor
 | |
| //
 | |
| NS_IMPL_ISUPPORTS(IOActivityMonitor, nsINamed)
 | |
| 
 | |
| IOActivityMonitor::IOActivityMonitor() : mLock("IOActivityMonitor::mLock") {
 | |
|   RefPtr<IOActivityMonitor> mon(gInstance);
 | |
|   MOZ_ASSERT(!mon, "multiple IOActivityMonitor instances!");
 | |
| }
 | |
| 
 | |
| // static
 | |
| void IOActivityMonitor::RequestActivities(dom::Promise* aPromise) {
 | |
|   MOZ_ASSERT(aPromise);
 | |
|   RefPtr<IOActivityMonitor> mon(gInstance);
 | |
|   if (!IsActive()) {
 | |
|     aPromise->MaybeReject(NS_ERROR_FAILURE);
 | |
|     return;
 | |
|   }
 | |
|   mon->RequestActivitiesInternal(aPromise);
 | |
| }
 | |
| 
 | |
| void IOActivityMonitor::RequestActivitiesInternal(dom::Promise* aPromise) {
 | |
|   nsresult result = NS_OK;
 | |
|   FallibleTArray<dom::IOActivityDataDictionary> activities;
 | |
| 
 | |
|   {
 | |
|     mozilla::MutexAutoLock lock(mLock);
 | |
|     // Remove inactive activities
 | |
|     for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
 | |
|       dom::IOActivityDataDictionary* activity = &iter.Data();
 | |
|       if (activity->mRx == 0 && activity->mTx == 0) {
 | |
|         iter.Remove();
 | |
|       } else {
 | |
|         if (NS_WARN_IF(!activities.AppendElement(iter.Data(), fallible))) {
 | |
|           result = NS_ERROR_OUT_OF_MEMORY;
 | |
|           break;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   if (NS_WARN_IF(NS_FAILED(result))) {
 | |
|     aPromise->MaybeReject(result);
 | |
|     return;
 | |
|   }
 | |
|   aPromise->MaybeResolve(activities);
 | |
| }
 | |
| 
 | |
| // static
 | |
| NS_IMETHODIMP
 | |
| IOActivityMonitor::GetName(nsACString& aName) {
 | |
|   aName.AssignLiteral("IOActivityMonitor");
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| bool IOActivityMonitor::IsActive() { return gInstance != nullptr; }
 | |
| 
 | |
| nsresult IOActivityMonitor::Init() {
 | |
|   if (IsActive()) {
 | |
|     return NS_ERROR_ALREADY_INITIALIZED;
 | |
|   }
 | |
|   RefPtr<IOActivityMonitor> mon = new IOActivityMonitor();
 | |
|   nsresult rv = mon->InitInternal();
 | |
|   if (NS_SUCCEEDED(rv)) {
 | |
|     gInstance = mon;
 | |
|   }
 | |
|   return rv;
 | |
| }
 | |
| 
 | |
| nsresult IOActivityMonitor::InitInternal() {
 | |
|   // 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;
 | |
|     sNetActivityMonitorLayerMethods.recv = nsNetMon_Recv;
 | |
|     sNetActivityMonitorLayerMethods.send = nsNetMon_Send;
 | |
|     sNetActivityMonitorLayerMethods.recvfrom = nsNetMon_RecvFrom;
 | |
|     sNetActivityMonitorLayerMethods.sendto = nsNetMon_SendTo;
 | |
|     sNetActivityMonitorLayerMethods.acceptread = nsNetMon_AcceptRead;
 | |
|     sNetActivityMonitorLayerMethods.close = nsNetMon_Close;
 | |
|     sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
 | |
|   }
 | |
| 
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult IOActivityMonitor::Shutdown() {
 | |
|   RefPtr<IOActivityMonitor> mon(gInstance);
 | |
|   if (!mon) {
 | |
|     return NS_ERROR_NOT_INITIALIZED;
 | |
|   }
 | |
|   return mon->ShutdownInternal();
 | |
| }
 | |
| 
 | |
| nsresult IOActivityMonitor::ShutdownInternal() {
 | |
|   mozilla::MutexAutoLock lock(mLock);
 | |
|   mActivities.Clear();
 | |
|   gInstance = nullptr;
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult IOActivityMonitor::MonitorSocket(PRFileDesc* aFd) {
 | |
|   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;
 | |
|   }
 | |
| 
 | |
|   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;
 | |
| }
 | |
| 
 | |
| bool IOActivityMonitor::IncrementActivity(const nsACString& aLocation,
 | |
|                                           uint32_t aRx, uint32_t aTx) {
 | |
|   mLock.AssertCurrentThreadOwns();
 | |
|   return mActivities.WithEntryHandle(aLocation, fallible, [&](auto&& entry) {
 | |
|     if (!entry) return false;
 | |
| 
 | |
|     if (*entry) {
 | |
|       // already registered
 | |
|       entry->Data().mTx += aTx;
 | |
|       entry->Data().mRx += aRx;
 | |
|     } else {
 | |
|       // 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);
 | |
| 
 | |
|       dom::IOActivityDataDictionary activity;
 | |
|       activity.mLocation.Assign(aLocation);
 | |
|       activity.mTx = aTx;
 | |
|       activity.mRx = aRx;
 | |
| 
 | |
|       entry->Insert(std::move(activity));
 | |
|     }
 | |
| 
 | |
|     return true;
 | |
|   });
 | |
| }
 | |
| 
 | |
| nsresult IOActivityMonitor::Write(const nsACString& aLocation,
 | |
|                                   uint32_t aAmount) {
 | |
|   RefPtr<IOActivityMonitor> mon(gInstance);
 | |
|   if (!mon) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   return mon->WriteInternal(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::WriteInternal(const nsACString& aLocation,
 | |
|                                           uint32_t aAmount) {
 | |
|   mozilla::MutexAutoLock lock(mLock);
 | |
|   if (!IncrementActivity(aLocation, aAmount, 0)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | |
| 
 | |
| nsresult IOActivityMonitor::Read(PRFileDesc* fd, uint32_t aAmount) {
 | |
|   RefPtr<IOActivityMonitor> mon(gInstance);
 | |
|   if (!mon) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   return mon->Read(FileDesc2Location(fd), aAmount);
 | |
| }
 | |
| 
 | |
| nsresult IOActivityMonitor::Read(const nsACString& aLocation,
 | |
|                                  uint32_t aAmount) {
 | |
|   RefPtr<IOActivityMonitor> mon(gInstance);
 | |
|   if (!mon) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   return mon->ReadInternal(aLocation, aAmount);
 | |
| }
 | |
| 
 | |
| nsresult IOActivityMonitor::ReadInternal(const nsACString& aLocation,
 | |
|                                          uint32_t aAmount) {
 | |
|   mozilla::MutexAutoLock lock(mLock);
 | |
|   if (!IncrementActivity(aLocation, 0, aAmount)) {
 | |
|     return NS_ERROR_FAILURE;
 | |
|   }
 | |
|   return NS_OK;
 | |
| }
 | 
