Source code

Revision control

Other Tools

1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
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/dom/Promise.h"
12
#include "prerror.h"
13
#include "prio.h"
14
#include "prmem.h"
15
#include <vector>
16
17
using namespace mozilla;
18
using namespace mozilla::net;
19
20
mozilla::StaticRefPtr<IOActivityMonitor> gInstance;
21
static PRDescIdentity sNetActivityMonitorLayerIdentity;
22
static PRIOMethods sNetActivityMonitorLayerMethods;
23
static PRIOMethods* sNetActivityMonitorLayerMethodsPtr = nullptr;
24
25
// Maximum number of activities entries in the monitoring class
26
#define MAX_ACTIVITY_ENTRIES 1000
27
28
// ActivityMonitorSecret is stored in the activity monitor layer
29
// and provides a method to get the location.
30
//
31
// A location can be :
32
// - a TCP or UDP socket. The form will be socket://ip:port
33
// - a File. The form will be file://path
34
//
35
// For other cases, the location will be fd://number
36
class ActivityMonitorSecret final {
37
public:
38
// constructor used for sockets
39
explicit ActivityMonitorSecret(PRFileDesc* aFd) {
40
mFd = aFd;
41
mLocationSet = false;
42
}
43
44
// constructor used for files
45
explicit ActivityMonitorSecret(PRFileDesc* aFd, const char* aLocation) {
46
mFd = aFd;
47
mLocation.AppendPrintf("file://%s", aLocation);
48
mLocationSet = true;
49
}
50
51
nsCString getLocation() {
52
if (!mLocationSet) {
53
LazySetLocation();
54
}
55
return mLocation;
56
}
57
58
private:
59
// Called to set the location using the FD on the first getLocation() usage
60
// which is typically when a socket is opened. If done earlier, at
61
// construction time, the host won't be bound yet.
62
//
63
// If the location is a file, it needs to be initialized in the
64
// constructor.
65
void LazySetLocation() {
66
mLocationSet = true;
67
PRFileDesc* extract = mFd;
68
while (PR_GetDescType(extract) == PR_DESC_LAYERED) {
69
if (!extract->lower) {
70
break;
71
}
72
extract = extract->lower;
73
}
74
75
PRDescType fdType = PR_GetDescType(extract);
76
// we should not use LazySetLocation for files
77
MOZ_ASSERT(fdType != PR_DESC_FILE);
78
79
switch (fdType) {
80
case PR_DESC_SOCKET_TCP:
81
case PR_DESC_SOCKET_UDP: {
82
mLocation.AppendPrintf("socket://");
83
PRNetAddr addr;
84
PRStatus status = PR_GetSockName(mFd, &addr);
85
if (NS_WARN_IF(status == PR_FAILURE)) {
86
mLocation.AppendPrintf("unknown");
87
break;
88
}
89
90
// grabbing the host
91
char netAddr[mozilla::net::kNetAddrMaxCStrBufSize] = {0};
92
status = PR_NetAddrToString(&addr, netAddr, sizeof(netAddr) - 1);
93
if (NS_WARN_IF(status == PR_FAILURE) || netAddr[0] == 0) {
94
mLocation.AppendPrintf("unknown");
95
break;
96
}
97
mLocation.Append(netAddr);
98
99
// adding the port
100
uint16_t port;
101
if (addr.raw.family == PR_AF_INET) {
102
port = addr.inet.port;
103
} else {
104
port = addr.ipv6.port;
105
}
106
mLocation.AppendPrintf(":%d", port);
107
} break;
108
109
// for all other cases, we just send back fd://<value>
110
default: {
111
mLocation.AppendPrintf("fd://%d", PR_FileDesc2NativeHandle(mFd));
112
}
113
} // end switch
114
}
115
116
private:
117
nsCString mLocation;
118
bool mLocationSet;
119
PRFileDesc* mFd;
120
};
121
122
// FileDesc2Location converts a PRFileDesc into a "location" by
123
// grabbing the ActivityMonitorSecret in layer->secret
124
static nsAutoCString FileDesc2Location(PRFileDesc* fd) {
125
nsAutoCString location;
126
PRFileDesc* monitorLayer =
127
PR_GetIdentitiesLayer(fd, sNetActivityMonitorLayerIdentity);
128
if (!monitorLayer) {
129
location.AppendPrintf("unknown");
130
return location;
131
}
132
133
ActivityMonitorSecret* secret = (ActivityMonitorSecret*)monitorLayer->secret;
134
location.AppendPrintf("%s", secret->getLocation().get());
135
return location;
136
}
137
138
//
139
// Wrappers around the socket APIS
140
//
141
static PRStatus nsNetMon_Connect(PRFileDesc* fd, const PRNetAddr* addr,
142
PRIntervalTime timeout) {
143
return fd->lower->methods->connect(fd->lower, addr, timeout);
144
}
145
146
static PRStatus nsNetMon_Close(PRFileDesc* fd) {
147
if (!fd) {
148
return PR_FAILURE;
149
}
150
PRFileDesc* layer = PR_PopIOLayer(fd, PR_TOP_IO_LAYER);
151
MOZ_RELEASE_ASSERT(
152
layer && layer->identity == sNetActivityMonitorLayerIdentity,
153
"NetActivityMonitor Layer not on top of stack");
154
155
if (layer->secret) {
156
delete (ActivityMonitorSecret*)layer->secret;
157
layer->secret = nullptr;
158
}
159
layer->dtor(layer);
160
return fd->methods->close(fd);
161
}
162
163
static int32_t nsNetMon_Read(PRFileDesc* fd, void* buf, int32_t len) {
164
int32_t ret = fd->lower->methods->read(fd->lower, buf, len);
165
if (ret >= 0) {
166
IOActivityMonitor::Read(fd, len);
167
}
168
return ret;
169
}
170
171
static int32_t nsNetMon_Write(PRFileDesc* fd, const void* buf, int32_t len) {
172
int32_t ret = fd->lower->methods->write(fd->lower, buf, len);
173
if (ret > 0) {
174
IOActivityMonitor::Write(fd, len);
175
}
176
return ret;
177
}
178
179
static int32_t nsNetMon_Writev(PRFileDesc* fd, const PRIOVec* iov, int32_t size,
180
PRIntervalTime timeout) {
181
int32_t ret = fd->lower->methods->writev(fd->lower, iov, size, timeout);
182
if (ret > 0) {
183
IOActivityMonitor::Write(fd, size);
184
}
185
return ret;
186
}
187
188
static int32_t nsNetMon_Recv(PRFileDesc* fd, void* buf, int32_t amount,
189
int flags, PRIntervalTime timeout) {
190
int32_t ret =
191
fd->lower->methods->recv(fd->lower, buf, amount, flags, timeout);
192
if (ret > 0) {
193
IOActivityMonitor::Read(fd, amount);
194
}
195
return ret;
196
}
197
198
static int32_t nsNetMon_Send(PRFileDesc* fd, const void* buf, int32_t amount,
199
int flags, PRIntervalTime timeout) {
200
int32_t ret =
201
fd->lower->methods->send(fd->lower, buf, amount, flags, timeout);
202
if (ret > 0) {
203
IOActivityMonitor::Write(fd, amount);
204
}
205
return ret;
206
}
207
208
static int32_t nsNetMon_RecvFrom(PRFileDesc* fd, void* buf, int32_t amount,
209
int flags, PRNetAddr* addr,
210
PRIntervalTime timeout) {
211
int32_t ret = fd->lower->methods->recvfrom(fd->lower, buf, amount, flags,
212
addr, timeout);
213
if (ret > 0) {
214
IOActivityMonitor::Read(fd, amount);
215
}
216
return ret;
217
}
218
219
static int32_t nsNetMon_SendTo(PRFileDesc* fd, const void* buf, int32_t amount,
220
int flags, const PRNetAddr* addr,
221
PRIntervalTime timeout) {
222
int32_t ret =
223
fd->lower->methods->sendto(fd->lower, buf, amount, flags, addr, timeout);
224
if (ret > 0) {
225
IOActivityMonitor::Write(fd, amount);
226
}
227
return ret;
228
}
229
230
static int32_t nsNetMon_AcceptRead(PRFileDesc* listenSock,
231
PRFileDesc** acceptedSock,
232
PRNetAddr** peerAddr, void* buf,
233
int32_t amount, PRIntervalTime timeout) {
234
int32_t ret = listenSock->lower->methods->acceptread(
235
listenSock->lower, acceptedSock, peerAddr, buf, amount, timeout);
236
if (ret > 0) {
237
IOActivityMonitor::Read(listenSock, amount);
238
}
239
return ret;
240
}
241
242
//
243
// Class IOActivityMonitor
244
//
245
NS_IMPL_ISUPPORTS(IOActivityMonitor, nsINamed)
246
247
IOActivityMonitor::IOActivityMonitor() : mLock("IOActivityMonitor::mLock") {
248
RefPtr<IOActivityMonitor> mon(gInstance);
249
MOZ_ASSERT(!mon, "multiple IOActivityMonitor instances!");
250
}
251
252
// static
253
void IOActivityMonitor::RequestActivities(dom::Promise* aPromise) {
254
MOZ_ASSERT(aPromise);
255
RefPtr<IOActivityMonitor> mon(gInstance);
256
if (!IsActive()) {
257
aPromise->MaybeReject(NS_ERROR_FAILURE);
258
return;
259
}
260
mon->RequestActivitiesInternal(aPromise);
261
}
262
263
void IOActivityMonitor::RequestActivitiesInternal(dom::Promise* aPromise) {
264
nsresult result = NS_OK;
265
FallibleTArray<dom::IOActivityDataDictionary> activities;
266
267
{
268
mozilla::MutexAutoLock lock(mLock);
269
// Remove inactive activities
270
for (auto iter = mActivities.Iter(); !iter.Done(); iter.Next()) {
271
dom::IOActivityDataDictionary* activity = &iter.Data();
272
if (activity->mRx == 0 && activity->mTx == 0) {
273
iter.Remove();
274
} else {
275
if (NS_WARN_IF(!activities.AppendElement(iter.Data(), fallible))) {
276
result = NS_ERROR_OUT_OF_MEMORY;
277
break;
278
}
279
}
280
}
281
}
282
283
if (NS_WARN_IF(NS_FAILED(result))) {
284
aPromise->MaybeReject(result);
285
return;
286
}
287
aPromise->MaybeResolve(activities);
288
}
289
290
// static
291
NS_IMETHODIMP
292
IOActivityMonitor::GetName(nsACString& aName) {
293
aName.AssignLiteral("IOActivityMonitor");
294
return NS_OK;
295
}
296
297
bool IOActivityMonitor::IsActive() { return gInstance != nullptr; }
298
299
nsresult IOActivityMonitor::Init() {
300
if (IsActive()) {
301
return NS_ERROR_ALREADY_INITIALIZED;
302
}
303
RefPtr<IOActivityMonitor> mon = new IOActivityMonitor();
304
nsresult rv = mon->InitInternal();
305
if (NS_SUCCEEDED(rv)) {
306
gInstance = mon;
307
}
308
return rv;
309
}
310
311
nsresult IOActivityMonitor::InitInternal() {
312
// wraps the socket APIs
313
if (!sNetActivityMonitorLayerMethodsPtr) {
314
sNetActivityMonitorLayerIdentity =
315
PR_GetUniqueIdentity("network activity monitor layer");
316
sNetActivityMonitorLayerMethods = *PR_GetDefaultIOMethods();
317
sNetActivityMonitorLayerMethods.connect = nsNetMon_Connect;
318
sNetActivityMonitorLayerMethods.read = nsNetMon_Read;
319
sNetActivityMonitorLayerMethods.write = nsNetMon_Write;
320
sNetActivityMonitorLayerMethods.writev = nsNetMon_Writev;
321
sNetActivityMonitorLayerMethods.recv = nsNetMon_Recv;
322
sNetActivityMonitorLayerMethods.send = nsNetMon_Send;
323
sNetActivityMonitorLayerMethods.recvfrom = nsNetMon_RecvFrom;
324
sNetActivityMonitorLayerMethods.sendto = nsNetMon_SendTo;
325
sNetActivityMonitorLayerMethods.acceptread = nsNetMon_AcceptRead;
326
sNetActivityMonitorLayerMethods.close = nsNetMon_Close;
327
sNetActivityMonitorLayerMethodsPtr = &sNetActivityMonitorLayerMethods;
328
}
329
330
return NS_OK;
331
}
332
333
nsresult IOActivityMonitor::Shutdown() {
334
RefPtr<IOActivityMonitor> mon(gInstance);
335
if (!mon) {
336
return NS_ERROR_NOT_INITIALIZED;
337
}
338
return mon->ShutdownInternal();
339
}
340
341
nsresult IOActivityMonitor::ShutdownInternal() {
342
mozilla::MutexAutoLock lock(mLock);
343
mActivities.Clear();
344
gInstance = nullptr;
345
return NS_OK;
346
}
347
348
nsresult IOActivityMonitor::MonitorSocket(PRFileDesc* aFd) {
349
RefPtr<IOActivityMonitor> mon(gInstance);
350
if (!IsActive()) {
351
return NS_OK;
352
}
353
PRFileDesc* layer;
354
PRStatus status;
355
layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity,
356
sNetActivityMonitorLayerMethodsPtr);
357
if (!layer) {
358
return NS_ERROR_FAILURE;
359
}
360
361
ActivityMonitorSecret* secret = new ActivityMonitorSecret(aFd);
362
layer->secret = reinterpret_cast<PRFilePrivate*>(secret);
363
status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer);
364
365
if (status == PR_FAILURE) {
366
delete secret;
367
PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
368
return NS_ERROR_FAILURE;
369
}
370
return NS_OK;
371
}
372
373
nsresult IOActivityMonitor::MonitorFile(PRFileDesc* aFd, const char* aPath) {
374
RefPtr<IOActivityMonitor> mon(gInstance);
375
if (!IsActive()) {
376
return NS_OK;
377
}
378
PRFileDesc* layer;
379
PRStatus status;
380
layer = PR_CreateIOLayerStub(sNetActivityMonitorLayerIdentity,
381
sNetActivityMonitorLayerMethodsPtr);
382
if (!layer) {
383
return NS_ERROR_FAILURE;
384
}
385
386
ActivityMonitorSecret* secret = new ActivityMonitorSecret(aFd, aPath);
387
layer->secret = reinterpret_cast<PRFilePrivate*>(secret);
388
389
status = PR_PushIOLayer(aFd, PR_NSPR_IO_LAYER, layer);
390
if (status == PR_FAILURE) {
391
delete secret;
392
PR_Free(layer); // PR_CreateIOLayerStub() uses PR_Malloc().
393
return NS_ERROR_FAILURE;
394
}
395
396
return NS_OK;
397
}
398
399
bool IOActivityMonitor::IncrementActivity(const nsACString& aLocation,
400
uint32_t aRx, uint32_t aTx) {
401
mLock.AssertCurrentThreadOwns();
402
if (auto entry = mActivities.Lookup(aLocation)) {
403
// already registered
404
entry.Data().mTx += aTx;
405
entry.Data().mRx += aRx;
406
return true;
407
}
408
// Creating a new IOActivity. Notice that mActivities will
409
// grow indefinitely, which is OK since we won't have
410
// but a few hundreds entries at the most, but we
411
// want to assert we have at the most 1000 entries
412
MOZ_ASSERT(mActivities.Count() < MAX_ACTIVITY_ENTRIES);
413
414
dom::IOActivityDataDictionary activity;
415
activity.mLocation.Assign(aLocation);
416
activity.mTx = aTx;
417
activity.mRx = aRx;
418
419
if (NS_WARN_IF(!mActivities.Put(aLocation, activity, fallible))) {
420
return false;
421
}
422
return true;
423
}
424
425
nsresult IOActivityMonitor::Write(const nsACString& aLocation,
426
uint32_t aAmount) {
427
RefPtr<IOActivityMonitor> mon(gInstance);
428
if (!mon) {
429
return NS_ERROR_FAILURE;
430
}
431
return mon->WriteInternal(aLocation, aAmount);
432
}
433
434
nsresult IOActivityMonitor::Write(PRFileDesc* fd, uint32_t aAmount) {
435
RefPtr<IOActivityMonitor> mon(gInstance);
436
if (!mon) {
437
return NS_ERROR_FAILURE;
438
}
439
return mon->Write(FileDesc2Location(fd), aAmount);
440
}
441
442
nsresult IOActivityMonitor::WriteInternal(const nsACString& aLocation,
443
uint32_t aAmount) {
444
mozilla::MutexAutoLock lock(mLock);
445
if (!IncrementActivity(aLocation, aAmount, 0)) {
446
return NS_ERROR_FAILURE;
447
}
448
return NS_OK;
449
}
450
451
nsresult IOActivityMonitor::Read(PRFileDesc* fd, uint32_t aAmount) {
452
RefPtr<IOActivityMonitor> mon(gInstance);
453
if (!mon) {
454
return NS_ERROR_FAILURE;
455
}
456
return mon->Read(FileDesc2Location(fd), aAmount);
457
}
458
459
nsresult IOActivityMonitor::Read(const nsACString& aLocation,
460
uint32_t aAmount) {
461
RefPtr<IOActivityMonitor> mon(gInstance);
462
if (!mon) {
463
return NS_ERROR_FAILURE;
464
}
465
return mon->ReadInternal(aLocation, aAmount);
466
}
467
468
nsresult IOActivityMonitor::ReadInternal(const nsACString& aLocation,
469
uint32_t aAmount) {
470
mozilla::MutexAutoLock lock(mLock);
471
if (!IncrementActivity(aLocation, 0, aAmount)) {
472
return NS_ERROR_FAILURE;
473
}
474
return NS_OK;
475
}