зеркало из https://github.com/mozilla/gecko-dev.git
478 строки
14 KiB
C++
478 строки
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 "nsIObserverService.h"
|
|
#include "nsPISocketTransportService.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();
|
|
if (auto entry = mActivities.Lookup(aLocation)) {
|
|
// already registered
|
|
entry.Data().mTx += aTx;
|
|
entry.Data().mRx += aRx;
|
|
return true;
|
|
}
|
|
// 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;
|
|
|
|
if (NS_WARN_IF(!mActivities.Put(aLocation, activity, fallible))) {
|
|
return false;
|
|
}
|
|
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;
|
|
}
|