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
|