1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "IOActivityMonitor.h"
7 #include "nsPrintfCString.h"
8 #include "nsSocketTransport2.h"
9 #include "nsSocketTransportService2.h"
10 #include "nsThreadUtils.h"
11 #include "mozilla/Atomics.h"
12 #include "mozilla/ClearOnShutdown.h"
13 #include "mozilla/dom/Promise.h"
19 using namespace mozilla
;
20 using namespace mozilla::net
;
22 mozilla::StaticRefPtr
<IOActivityMonitor
> gInstance
;
23 static Atomic
<bool> gActivated(false);
24 static PRDescIdentity sNetActivityMonitorLayerIdentity
;
25 static PRIOMethods sNetActivityMonitorLayerMethods
;
26 static PRIOMethods
* sNetActivityMonitorLayerMethodsPtr
= nullptr;
28 // Maximum number of activities entries in the monitoring class
29 #define MAX_ACTIVITY_ENTRIES 1000
31 // ActivityMonitorSecret is stored in the activity monitor layer
32 // and provides a method to get the location.
34 // A location can be :
35 // - a TCP or UDP socket. The form will be socket://ip:port
36 // - a File. The form will be file://path
38 // For other cases, the location will be fd://number
39 class ActivityMonitorSecret final
{
41 // constructor used for sockets
42 explicit ActivityMonitorSecret(PRFileDesc
* aFd
) {
47 // constructor used for files
48 explicit ActivityMonitorSecret(PRFileDesc
* aFd
, const char* aLocation
) {
50 mLocation
.AppendPrintf("file://%s", aLocation
);
54 nsCString
getLocation() {
62 // Called to set the location using the FD on the first getLocation() usage
63 // which is typically when a socket is opened. If done earlier, at
64 // construction time, the host won't be bound yet.
66 // If the location is a file, it needs to be initialized in the
68 void LazySetLocation() {
70 PRFileDesc
* extract
= mFd
;
71 while (PR_GetDescType(extract
) == PR_DESC_LAYERED
) {
72 if (!extract
->lower
) {
75 extract
= extract
->lower
;
78 PRDescType fdType
= PR_GetDescType(extract
);
79 // we should not use LazySetLocation for files
80 MOZ_ASSERT(fdType
!= PR_DESC_FILE
);
83 case PR_DESC_SOCKET_TCP
:
84 case PR_DESC_SOCKET_UDP
: {
85 mLocation
.AppendPrintf("socket://");
87 PRStatus status
= PR_GetSockName(mFd
, &addr
);
88 if (NS_WARN_IF(status
== PR_FAILURE
)) {
89 mLocation
.AppendPrintf("unknown");
94 char netAddr
[mozilla::net::kNetAddrMaxCStrBufSize
] = {0};
95 status
= PR_NetAddrToString(&addr
, netAddr
, sizeof(netAddr
) - 1);
96 if (NS_WARN_IF(status
== PR_FAILURE
) || netAddr
[0] == 0) {
97 mLocation
.AppendPrintf("unknown");
100 mLocation
.Append(netAddr
);
104 if (addr
.raw
.family
== PR_AF_INET
) {
105 port
= addr
.inet
.port
;
107 port
= addr
.ipv6
.port
;
109 mLocation
.AppendPrintf(":%d", port
);
112 // for all other cases, we just send back fd://<value>
114 mLocation
.AppendLiteral("fd://");
115 mLocation
.AppendInt(PR_FileDesc2NativeHandle(mFd
));
126 // FileDesc2Location converts a PRFileDesc into a "location" by
127 // grabbing the ActivityMonitorSecret in layer->secret
128 static nsAutoCString
FileDesc2Location(PRFileDesc
* fd
) {
129 nsAutoCString location
;
130 PRFileDesc
* monitorLayer
=
131 PR_GetIdentitiesLayer(fd
, sNetActivityMonitorLayerIdentity
);
133 location
.AppendPrintf("unknown");
137 ActivityMonitorSecret
* secret
= (ActivityMonitorSecret
*)monitorLayer
->secret
;
138 location
.AppendPrintf("%s", secret
->getLocation().get());
143 // Wrappers around the socket APIS
145 static PRStatus
nsNetMon_Connect(PRFileDesc
* fd
, const PRNetAddr
* addr
,
146 PRIntervalTime timeout
) {
147 return fd
->lower
->methods
->connect(fd
->lower
, addr
, timeout
);
150 static PRStatus
nsNetMon_Close(PRFileDesc
* fd
) {
154 PRFileDesc
* layer
= PR_PopIOLayer(fd
, PR_TOP_IO_LAYER
);
156 layer
&& layer
->identity
== sNetActivityMonitorLayerIdentity
,
157 "NetActivityMonitor Layer not on top of stack");
160 delete (ActivityMonitorSecret
*)layer
->secret
;
161 layer
->secret
= nullptr;
164 return fd
->methods
->close(fd
);
167 static int32_t nsNetMon_Read(PRFileDesc
* fd
, void* buf
, int32_t len
) {
168 int32_t ret
= fd
->lower
->methods
->read(fd
->lower
, buf
, len
);
170 IOActivityMonitor::Read(fd
, len
);
175 static int32_t nsNetMon_Write(PRFileDesc
* fd
, const void* buf
, int32_t len
) {
176 int32_t ret
= fd
->lower
->methods
->write(fd
->lower
, buf
, len
);
178 IOActivityMonitor::Write(fd
, len
);
183 static int32_t nsNetMon_Writev(PRFileDesc
* fd
, const PRIOVec
* iov
, int32_t size
,
184 PRIntervalTime timeout
) {
185 int32_t ret
= fd
->lower
->methods
->writev(fd
->lower
, iov
, size
, timeout
);
187 IOActivityMonitor::Write(fd
, size
);
192 static int32_t nsNetMon_Recv(PRFileDesc
* fd
, void* buf
, int32_t amount
,
193 int flags
, PRIntervalTime timeout
) {
195 fd
->lower
->methods
->recv(fd
->lower
, buf
, amount
, flags
, timeout
);
197 IOActivityMonitor::Read(fd
, amount
);
202 static int32_t nsNetMon_Send(PRFileDesc
* fd
, const void* buf
, int32_t amount
,
203 int flags
, PRIntervalTime timeout
) {
205 fd
->lower
->methods
->send(fd
->lower
, buf
, amount
, flags
, timeout
);
207 IOActivityMonitor::Write(fd
, amount
);
212 static int32_t nsNetMon_RecvFrom(PRFileDesc
* fd
, void* buf
, int32_t amount
,
213 int flags
, PRNetAddr
* addr
,
214 PRIntervalTime timeout
) {
215 int32_t ret
= fd
->lower
->methods
->recvfrom(fd
->lower
, buf
, amount
, flags
,
218 IOActivityMonitor::Read(fd
, amount
);
223 static int32_t nsNetMon_SendTo(PRFileDesc
* fd
, const void* buf
, int32_t amount
,
224 int flags
, const PRNetAddr
* addr
,
225 PRIntervalTime timeout
) {
227 fd
->lower
->methods
->sendto(fd
->lower
, buf
, amount
, flags
, addr
, timeout
);
229 IOActivityMonitor::Write(fd
, amount
);
234 static int32_t nsNetMon_AcceptRead(PRFileDesc
* listenSock
,
235 PRFileDesc
** acceptedSock
,
236 PRNetAddr
** peerAddr
, void* buf
,
237 int32_t amount
, PRIntervalTime timeout
) {
238 int32_t ret
= listenSock
->lower
->methods
->acceptread(
239 listenSock
->lower
, acceptedSock
, peerAddr
, buf
, amount
, timeout
);
241 IOActivityMonitor::Read(listenSock
, amount
);
247 // Class IOActivityMonitor
249 NS_IMPL_ISUPPORTS(IOActivityMonitor
, nsINamed
)
251 IOActivityMonitor::IOActivityMonitor() : mLock("IOActivityMonitor::mLock") {
252 RefPtr
<IOActivityMonitor
> mon(gInstance
);
253 MOZ_ASSERT(!mon
, "multiple IOActivityMonitor instances!");
257 void IOActivityMonitor::RequestActivities(dom::Promise
* aPromise
) {
258 MOZ_ASSERT(aPromise
);
259 RefPtr
<IOActivityMonitor
> mon
= IOActivityMonitor::Get();
261 aPromise
->MaybeReject(NS_ERROR_FAILURE
);
265 mon
->RequestActivitiesInternal(aPromise
);
268 void IOActivityMonitor::RequestActivitiesInternal(dom::Promise
* aPromise
) {
269 nsresult result
= NS_OK
;
270 FallibleTArray
<dom::IOActivityDataDictionary
> activities
;
273 mozilla::MutexAutoLock
lock(mLock
);
274 // Remove inactive activities
275 for (auto iter
= mActivities
.Iter(); !iter
.Done(); iter
.Next()) {
276 dom::IOActivityDataDictionary
* activity
= &iter
.Data();
277 if (activity
->mRx
== 0 && activity
->mTx
== 0) {
280 if (NS_WARN_IF(!activities
.AppendElement(iter
.Data(), fallible
))) {
281 result
= NS_ERROR_OUT_OF_MEMORY
;
288 if (NS_WARN_IF(NS_FAILED(result
))) {
289 aPromise
->MaybeReject(result
);
292 aPromise
->MaybeResolve(activities
);
296 IOActivityMonitor::GetName(nsACString
& aName
) {
297 aName
.AssignLiteral("IOActivityMonitor");
302 bool IOActivityMonitor::IsActive() { return gActivated
; }
305 already_AddRefed
<IOActivityMonitor
> IOActivityMonitor::Get() {
310 RefPtr
<IOActivityMonitor
> mon
= gInstance
;
314 nsresult
IOActivityMonitor::Init() {
316 return NS_ERROR_ALREADY_INITIALIZED
;
318 RefPtr
<IOActivityMonitor
> mon
= new IOActivityMonitor();
319 nsresult rv
= mon
->InitInternal();
320 if (NS_SUCCEEDED(rv
)) {
322 ClearOnShutdown(&gInstance
);
328 nsresult
IOActivityMonitor::InitInternal() {
329 // wraps the socket APIs
330 if (!sNetActivityMonitorLayerMethodsPtr
) {
331 sNetActivityMonitorLayerIdentity
=
332 PR_GetUniqueIdentity("network activity monitor layer");
333 sNetActivityMonitorLayerMethods
= *PR_GetDefaultIOMethods();
334 sNetActivityMonitorLayerMethods
.connect
= nsNetMon_Connect
;
335 sNetActivityMonitorLayerMethods
.read
= nsNetMon_Read
;
336 sNetActivityMonitorLayerMethods
.write
= nsNetMon_Write
;
337 sNetActivityMonitorLayerMethods
.writev
= nsNetMon_Writev
;
338 sNetActivityMonitorLayerMethods
.recv
= nsNetMon_Recv
;
339 sNetActivityMonitorLayerMethods
.send
= nsNetMon_Send
;
340 sNetActivityMonitorLayerMethods
.recvfrom
= nsNetMon_RecvFrom
;
341 sNetActivityMonitorLayerMethods
.sendto
= nsNetMon_SendTo
;
342 sNetActivityMonitorLayerMethods
.acceptread
= nsNetMon_AcceptRead
;
343 sNetActivityMonitorLayerMethods
.close
= nsNetMon_Close
;
344 sNetActivityMonitorLayerMethodsPtr
= &sNetActivityMonitorLayerMethods
;
350 nsresult
IOActivityMonitor::Shutdown() {
351 RefPtr
<IOActivityMonitor
> mon
= IOActivityMonitor::Get();
353 return NS_ERROR_NOT_INITIALIZED
;
355 return mon
->ShutdownInternal();
358 nsresult
IOActivityMonitor::ShutdownInternal() {
359 mozilla::MutexAutoLock
lock(mLock
);
365 nsresult
IOActivityMonitor::MonitorSocket(PRFileDesc
* aFd
) {
366 RefPtr
<IOActivityMonitor
> mon
= IOActivityMonitor::Get();
372 layer
= PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity
,
373 sNetActivityMonitorLayerMethodsPtr
);
375 return NS_ERROR_FAILURE
;
378 ActivityMonitorSecret
* secret
= new ActivityMonitorSecret(aFd
);
379 layer
->secret
= reinterpret_cast<PRFilePrivate
*>(secret
);
380 status
= PR_PushIOLayer(aFd
, PR_NSPR_IO_LAYER
, layer
);
382 if (status
== PR_FAILURE
) {
384 PR_Free(layer
); // PR_CreateIOLayerStub() uses PR_Malloc().
385 return NS_ERROR_FAILURE
;
390 nsresult
IOActivityMonitor::MonitorFile(PRFileDesc
* aFd
, const char* aPath
) {
391 RefPtr
<IOActivityMonitor
> mon
= IOActivityMonitor::Get();
397 layer
= PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity
,
398 sNetActivityMonitorLayerMethodsPtr
);
400 return NS_ERROR_FAILURE
;
403 ActivityMonitorSecret
* secret
= new ActivityMonitorSecret(aFd
, aPath
);
404 layer
->secret
= reinterpret_cast<PRFilePrivate
*>(secret
);
406 status
= PR_PushIOLayer(aFd
, PR_NSPR_IO_LAYER
, layer
);
407 if (status
== PR_FAILURE
) {
409 PR_Free(layer
); // PR_CreateIOLayerStub() uses PR_Malloc().
410 return NS_ERROR_FAILURE
;
416 bool IOActivityMonitor::IncrementActivity(const nsACString
& aLocation
,
417 uint32_t aRx
, uint32_t aTx
) {
418 mLock
.AssertCurrentThreadOwns();
419 return mActivities
.WithEntryHandle(aLocation
, fallible
, [&](auto&& entry
) {
420 if (!entry
) return false;
423 // already registered
424 entry
->Data().mTx
+= aTx
;
425 entry
->Data().mRx
+= aRx
;
427 // Creating a new IOActivity. Notice that mActivities
428 // will grow indefinitely, which is OK since we won't
429 // have but a few hundreds entries at the most, but we
430 // want to assert we have at the most 1000 entries
431 MOZ_ASSERT(mActivities
.Count() <= MAX_ACTIVITY_ENTRIES
);
433 dom::IOActivityDataDictionary activity
;
434 activity
.mLocation
.Assign(aLocation
);
438 entry
->Insert(std::move(activity
));
445 nsresult
IOActivityMonitor::Write(const nsACString
& aLocation
,
447 RefPtr
<IOActivityMonitor
> mon
= IOActivityMonitor::Get();
449 return NS_ERROR_FAILURE
;
451 return mon
->WriteInternal(aLocation
, aAmount
);
454 nsresult
IOActivityMonitor::Write(PRFileDesc
* fd
, uint32_t aAmount
) {
455 RefPtr
<IOActivityMonitor
> mon
= IOActivityMonitor::Get();
457 return NS_ERROR_FAILURE
;
459 return mon
->Write(FileDesc2Location(fd
), aAmount
);
462 nsresult
IOActivityMonitor::WriteInternal(const nsACString
& aLocation
,
464 mozilla::MutexAutoLock
lock(mLock
);
465 if (!IncrementActivity(aLocation
, aAmount
, 0)) {
466 return NS_ERROR_FAILURE
;
471 nsresult
IOActivityMonitor::Read(PRFileDesc
* fd
, uint32_t aAmount
) {
472 RefPtr
<IOActivityMonitor
> mon
= IOActivityMonitor::Get();
474 return NS_ERROR_FAILURE
;
476 return mon
->Read(FileDesc2Location(fd
), aAmount
);
479 nsresult
IOActivityMonitor::Read(const nsACString
& aLocation
,
481 RefPtr
<IOActivityMonitor
> mon
= IOActivityMonitor::Get();
483 return NS_ERROR_FAILURE
;
485 return mon
->ReadInternal(aLocation
, aAmount
);
488 nsresult
IOActivityMonitor::ReadInternal(const nsACString
& aLocation
,
490 mozilla::MutexAutoLock
lock(mLock
);
491 if (!IncrementActivity(aLocation
, 0, aAmount
)) {
492 return NS_ERROR_FAILURE
;