summaryrefslogtreecommitdiffstats
path: root/toolkit/components/perfmonitoring/PerformanceUtils.cpp
blob: 08ee695854d075af07906b171cc4080d14fa8e15 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#include "mozilla/PerformanceUtils.h"

#include "mozilla/dom/DOMTypes.h"
#include "mozilla/dom/DocGroup.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/WorkerDebugger.h"
#include "mozilla/dom/WorkerDebuggerManager.h"

#include "MediaDecoder.h"
#include "jsfriendapi.h"
#include "nsGlobalWindowOuter.h"
#include "nsWindowSizes.h"

using namespace mozilla;
using namespace mozilla::dom;

namespace mozilla {

nsTArray<RefPtr<PerformanceInfoPromise>> CollectPerformanceInfo() {
  nsTArray<RefPtr<PerformanceInfoPromise>> promises;

  // collecting ReportPerformanceInfo from all WorkerDebugger instances
  RefPtr<mozilla::dom::WorkerDebuggerManager> wdm =
      WorkerDebuggerManager::GetOrCreate();
  if (NS_WARN_IF(!wdm)) {
    return promises;
  }

  for (uint32_t i = 0; i < wdm->GetDebuggersLength(); i++) {
    const RefPtr<WorkerDebugger> debugger = wdm->GetDebuggerAt(i);
    promises.AppendElement(debugger->ReportPerformanceInfo());
  }

  nsTArray<RefPtr<BrowsingContextGroup>> groups;
  BrowsingContextGroup::GetAllGroups(groups);

  nsTArray<DocGroup*> docGroups;
  for (auto& browsingContextGroup : groups) {
    browsingContextGroup->GetDocGroups(docGroups);
  }

  for (DocGroup* docGroup : docGroups) {
    promises.AppendElement(docGroup->ReportPerformanceInfo());
  }

  return promises;
}

static void AddWindowTabSizes(nsGlobalWindowOuter* aWindow,
                              nsTabSizes* aSizes) {
  Document* document = aWindow->GetDocument();
  if (document && document->GetCachedSizes(aSizes)) {
    // We got a cached version
    return;
  }
  // We measure the sizes on a fresh nsTabSizes instance
  // because we want to cache the value and aSizes might
  // already have some values from other windows.
  nsTabSizes sizes;

  // Measure the window.
  SizeOfState state(moz_malloc_size_of);
  nsWindowSizes windowSizes(state);
  aWindow->AddSizeOfIncludingThis(windowSizes);
  // Measure the inner window, if there is one.
  nsGlobalWindowInner* inner = aWindow->GetCurrentInnerWindowInternal();
  if (inner != nullptr) {
    inner->AddSizeOfIncludingThis(windowSizes);
  }
  windowSizes.addToTabSizes(&sizes);
  if (document) {
    document->SetCachedSizes(&sizes);
  }
  aSizes->mDom += sizes.mDom;
  aSizes->mStyle += sizes.mStyle;
  aSizes->mOther += sizes.mOther;
}

RefPtr<MemoryPromise> CollectMemoryInfo(
    const RefPtr<DocGroup>& aDocGroup,
    const RefPtr<AbstractThread>& aEventTarget) {
  // Getting Dom sizes.
  nsTabSizes sizes;

  using WindowSet = mozilla::HashSet<nsGlobalWindowOuter*>;
  WindowSet windowsVisited;
  for (const auto& document : *aDocGroup) {
    nsGlobalWindowOuter* window =
        document ? nsGlobalWindowOuter::Cast(document->GetWindow()) : nullptr;
    if (window) {
      WindowSet::AddPtr p = windowsVisited.lookupForAdd(window);
      if (!p) {
        // We have not seen this window before.
        AddWindowTabSizes(window, &sizes);
        if (!windowsVisited.add(p, window)) {
          // OOM.  Let us stop counting memory, we may undercount.
          break;
        }
      }
    }
  }

  using ZoneSet = mozilla::HashSet<JS::Zone*>;
  using SharedSet = mozilla::HashSet<void*>;
  ZoneSet zonesVisited;
  SharedSet sharedVisited;
  // Getting JS-related memory usage
  uint64_t jsMemUsed = 0;
  nsTArray<RefPtr<MediaMemoryPromise>> mediaMemoryPromises;
  for (auto* doc : *aDocGroup) {
    bool unused;
    nsIGlobalObject* globalObject = doc->GetScriptHandlingObject(unused);
    if (globalObject) {
      JSObject* object = globalObject->GetGlobalJSObject();
      if (object != nullptr) {
        MOZ_ASSERT(NS_IsMainThread(),
                   "We cannot get the object zone on another thread");
        JS::Zone* zone = JS::GetObjectZone(object);
        ZoneSet::AddPtr addZone = zonesVisited.lookupForAdd(zone);
        if (!addZone) {
          // We have not checked this zone before.
          jsMemUsed += js::GetMemoryUsageForZone(zone);
          if (!zonesVisited.add(addZone, zone)) {
            // OOM.  Let us stop counting memory, we may undercount.
            break;
          }

          const js::gc::SharedMemoryMap& shared =
              js::GetSharedMemoryUsageForZone(zone);
          for (auto iter = shared.iter(); !iter.done(); iter.next()) {
            void* sharedMem = iter.get().key();
            SharedSet::AddPtr addShared = sharedVisited.lookupForAdd(sharedMem);
            if (addShared) {
              // We *have* seen this shared memory before.

              // Because shared memory is already included in
              // js::GetMemoryUsageForZone() above, and we've seen it for a
              // previous zone, we subtract it here so it's not counted more
              // than once.
              jsMemUsed -= iter.get().value().nbytes;
            } else if (!sharedVisited.add(addShared, sharedMem)) {
              // As above, abort with an under-estimate.
              break;
            }
          }
        }
      }
    }

    mediaMemoryPromises.AppendElement(GetMediaMemorySizes(doc));
  }

  // Getting Media sizes.
  return MediaMemoryPromise::All(aEventTarget, mediaMemoryPromises)
      ->Then(
          aEventTarget, __func__,
          [jsMemUsed, sizes](const nsTArray<MediaMemoryInfo> mediaArray) {
            size_t audioSize = 0;
            size_t videoSize = 0;
            size_t resourcesSize = 0;

            for (auto media : mediaArray) {
              audioSize += media.audioSize();
              videoSize += media.videoSize();
              resourcesSize += media.resourcesSize();
            }

            return MemoryPromise::CreateAndResolve(
                PerformanceMemoryInfo(
                    MediaMemoryInfo(audioSize, videoSize, resourcesSize),
                    sizes.mDom, sizes.mStyle, sizes.mOther, jsMemUsed),
                __func__);
          },
          [](const nsresult rv) {
            return MemoryPromise::CreateAndReject(rv, __func__);
          });
}

}  // namespace mozilla