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