Backed out 2 changesets (bug 1855992) for causing talos failures @ mozilla::net:...
[gecko.git] / netwerk / base / IOActivityMonitor.cpp
blob76047a7a00946b6fb445e4f641dca112bd4e6d3e
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"
14 #include "prerror.h"
15 #include "prio.h"
16 #include "prmem.h"
17 #include <vector>
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 {
40 public:
41 // constructor used for sockets
42 explicit ActivityMonitorSecret(PRFileDesc* aFd) {
43 mFd = aFd;
44 mLocationSet = false;
47 // constructor used for files
48 explicit ActivityMonitorSecret(PRFileDesc* aFd, const char* aLocation) {
49 mFd = aFd;
50 mLocation.AppendPrintf("file://%s", aLocation);
51 mLocationSet = true;
54 nsCString getLocation() {
55 if (!mLocationSet) {
56 LazySetLocation();
58 return mLocation;
61 private:
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
67 // constructor.
68 void LazySetLocation() {
69 mLocationSet = true;
70 PRFileDesc* extract = mFd;
71 while (PR_GetDescType(extract) == PR_DESC_LAYERED) {
72 if (!extract->lower) {
73 break;
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);
82 switch (fdType) {
83 case PR_DESC_SOCKET_TCP:
84 case PR_DESC_SOCKET_UDP: {
85 mLocation.AppendPrintf("socket://");
86 PRNetAddr addr;
87 PRStatus status = PR_GetSockName(mFd, &addr);
88 if (NS_WARN_IF(status == PR_FAILURE)) {
89 mLocation.AppendPrintf("unknown");
90 break;
93 // grabbing the host
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");
98 break;
100 mLocation.Append(netAddr);
102 // adding the port
103 uint16_t port;
104 if (addr.raw.family == PR_AF_INET) {
105 port = addr.inet.port;
106 } else {
107 port = addr.ipv6.port;
109 mLocation.AppendPrintf(":%d", port);
110 } break;
112 // for all other cases, we just send back fd://<value>
113 default: {
114 mLocation.AppendLiteral("fd://");
115 mLocation.AppendInt(PR_FileDesc2NativeHandle(mFd));
117 } // end switch
120 private:
121 nsCString mLocation;
122 bool mLocationSet;
123 PRFileDesc* 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);
132 if (!monitorLayer) {
133 location.AppendPrintf("unknown");
134 return location;
137 ActivityMonitorSecret* secret = (ActivityMonitorSecret*)monitorLayer->secret;
138 location.AppendPrintf("%s", secret->getLocation().get());
139 return location;
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) {
151 if (!fd) {
152 return PR_FAILURE;
154 PRFileDesc* layer = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
155 MOZ_RELEASE_ASSERT(
156 layer && layer->identity == sNetActivityMonitorLayerIdentity,
157 "NetActivityMonitor Layer not on top of stack");
159 if (layer->secret) {
160 delete (ActivityMonitorSecret*)layer->secret;
161 layer->secret = nullptr;
163 layer->dtor(layer);
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);
169 if (ret >= 0) {
170 IOActivityMonitor::Read(fd, len);
172 return ret;
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);
177 if (ret > 0) {
178 IOActivityMonitor::Write(fd, len);
180 return ret;
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);
186 if (ret > 0) {
187 IOActivityMonitor::Write(fd, size);
189 return ret;
192 static int32_t nsNetMon_Recv(PRFileDesc* fd, void* buf, int32_t amount,
193 int flags, PRIntervalTime timeout) {
194 int32_t ret =
195 fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout);
196 if (ret > 0) {
197 IOActivityMonitor::Read(fd, amount);
199 return ret;
202 static int32_t nsNetMon_Send(PRFileDesc* fd, const void* buf, int32_t amount,
203 int flags, PRIntervalTime timeout) {
204 int32_t ret =
205 fd->lower->methods->send(fd->lower, buf, amount, flags, timeout);
206 if (ret > 0) {
207 IOActivityMonitor::Write(fd, amount);
209 return ret;
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,
216 addr, timeout);
217 if (ret > 0) {
218 IOActivityMonitor::Read(fd, amount);
220 return ret;
223 static int32_t nsNetMon_SendTo(PRFileDesc* fd, const void* buf, int32_t amount,
224 int flags, const PRNetAddr* addr,
225 PRIntervalTime timeout) {
226 int32_t ret =
227 fd->lower->methods->sendto(fd->lower, buf, amount, flags, addr, timeout);
228 if (ret > 0) {
229 IOActivityMonitor::Write(fd, amount);
231 return ret;
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);
240 if (ret > 0) {
241 IOActivityMonitor::Read(listenSock, amount);
243 return ret;
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!");
256 // static
257 void IOActivityMonitor::RequestActivities(dom::Promise* aPromise) {
258 MOZ_ASSERT(aPromise);
259 RefPtr<IOActivityMonitor> mon = IOActivityMonitor::Get();
260 if (!mon) {
261 aPromise->MaybeReject(NS_ERROR_FAILURE);
262 return;
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) {
278 iter.Remove();
279 } else {
280 if (NS_WARN_IF(!activities.AppendElement(iter.Data(), fallible))) {
281 result = NS_ERROR_OUT_OF_MEMORY;
282 break;
288 if (NS_WARN_IF(NS_FAILED(result))) {
289 aPromise->MaybeReject(result);
290 return;
292 aPromise->MaybeResolve(activities);
295 NS_IMETHODIMP
296 IOActivityMonitor::GetName(nsACString& aName) {
297 aName.AssignLiteral("IOActivityMonitor");
298 return NS_OK;
301 // static
302 bool IOActivityMonitor::IsActive() { return gActivated; }
304 // static
305 already_AddRefed<IOActivityMonitor> IOActivityMonitor::Get() {
306 if (!gActivated) {
307 return nullptr;
310 RefPtr<IOActivityMonitor> mon = gInstance;
311 return mon.forget();
314 nsresult IOActivityMonitor::Init() {
315 if (IsActive()) {
316 return NS_ERROR_ALREADY_INITIALIZED;
318 RefPtr<IOActivityMonitor> mon = new IOActivityMonitor();
319 nsresult rv = mon->InitInternal();
320 if (NS_SUCCEEDED(rv)) {
321 gInstance = mon;
322 ClearOnShutdown(&gInstance);
323 gActivated = true;
325 return rv;
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;
347 return NS_OK;
350 nsresult IOActivityMonitor::Shutdown() {
351 RefPtr<IOActivityMonitor> mon = IOActivityMonitor::Get();
352 if (!mon) {
353 return NS_ERROR_NOT_INITIALIZED;
355 return mon->ShutdownInternal();
358 nsresult IOActivityMonitor::ShutdownInternal() {
359 mozilla::MutexAutoLock lock(mLock);
360 mActivities.Clear();
361 gActivated = false;
362 return NS_OK;
365 nsresult IOActivityMonitor::MonitorSocket(PRFileDesc* aFd) {
366 RefPtr<IOActivityMonitor> mon = IOActivityMonitor::Get();
367 if (!mon) {
368 return NS_OK;
370 PRFileDesc* layer;
371 PRStatus status;
372 layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity,
373 sNetActivityMonitorLayerMethodsPtr);
374 if (!layer) {
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) {
383 delete secret;
384 PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
385 return NS_ERROR_FAILURE;
387 return NS_OK;
390 nsresult IOActivityMonitor::MonitorFile(PRFileDesc* aFd, const char* aPath) {
391 RefPtr<IOActivityMonitor> mon = IOActivityMonitor::Get();
392 if (!mon) {
393 return NS_OK;
395 PRFileDesc* layer;
396 PRStatus status;
397 layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity,
398 sNetActivityMonitorLayerMethodsPtr);
399 if (!layer) {
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) {
408 delete secret;
409 PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
410 return NS_ERROR_FAILURE;
413 return NS_OK;
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;
422 if (*entry) {
423 // already registered
424 entry->Data().mTx += aTx;
425 entry->Data().mRx += aRx;
426 } else {
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);
435 activity.mTx = aTx;
436 activity.mRx = aRx;
438 entry->Insert(std::move(activity));
441 return true;
445 nsresult IOActivityMonitor::Write(const nsACString& aLocation,
446 uint32_t aAmount) {
447 RefPtr<IOActivityMonitor> mon = IOActivityMonitor::Get();
448 if (!mon) {
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();
456 if (!mon) {
457 return NS_ERROR_FAILURE;
459 return mon->Write(FileDesc2Location(fd), aAmount);
462 nsresult IOActivityMonitor::WriteInternal(const nsACString& aLocation,
463 uint32_t aAmount) {
464 mozilla::MutexAutoLock lock(mLock);
465 if (!IncrementActivity(aLocation, aAmount, 0)) {
466 return NS_ERROR_FAILURE;
468 return NS_OK;
471 nsresult IOActivityMonitor::Read(PRFileDesc* fd, uint32_t aAmount) {
472 RefPtr<IOActivityMonitor> mon = IOActivityMonitor::Get();
473 if (!mon) {
474 return NS_ERROR_FAILURE;
476 return mon->Read(FileDesc2Location(fd), aAmount);
479 nsresult IOActivityMonitor::Read(const nsACString& aLocation,
480 uint32_t aAmount) {
481 RefPtr<IOActivityMonitor> mon = IOActivityMonitor::Get();
482 if (!mon) {
483 return NS_ERROR_FAILURE;
485 return mon->ReadInternal(aLocation, aAmount);
488 nsresult IOActivityMonitor::ReadInternal(const nsACString& aLocation,
489 uint32_t aAmount) {
490 mozilla::MutexAutoLock lock(mLock);
491 if (!IncrementActivity(aLocation, 0, aAmount)) {
492 return NS_ERROR_FAILURE;
494 return NS_OK;