Coverage Report

Created: 2018-09-25 14:53

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