mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 929797 - Implement proper memory reporting for child processes. r=khuey.
--HG-- extra : rebase_source : f0788757afc2097830295170120e1e2094993cc7
This commit is contained in:
parent
fbfefcc32a
commit
a277702631
@ -433,7 +433,7 @@ ContentChild::InitXPCOM()
|
||||
}
|
||||
|
||||
PMemoryReportRequestChild*
|
||||
ContentChild::AllocPMemoryReportRequestChild()
|
||||
ContentChild::AllocPMemoryReportRequestChild(const uint32_t& generation)
|
||||
{
|
||||
return new MemoryReportRequestChild();
|
||||
}
|
||||
@ -480,7 +480,9 @@ NS_IMPL_ISUPPORTS1(
|
||||
)
|
||||
|
||||
bool
|
||||
ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* child)
|
||||
ContentChild::RecvPMemoryReportRequestConstructor(
|
||||
PMemoryReportRequestChild* child,
|
||||
const uint32_t& generation)
|
||||
{
|
||||
nsCOMPtr<nsIMemoryReporterManager> mgr = do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
|
||||
@ -504,7 +506,7 @@ ContentChild::RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* chi
|
||||
r->CollectReports(cb, wrappedReports);
|
||||
}
|
||||
|
||||
child->Send__delete__(child, reports);
|
||||
child->Send__delete__(child, generation, reports);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -111,13 +111,14 @@ public:
|
||||
virtual bool DeallocPIndexedDBChild(PIndexedDBChild* aActor);
|
||||
|
||||
virtual PMemoryReportRequestChild*
|
||||
AllocPMemoryReportRequestChild();
|
||||
AllocPMemoryReportRequestChild(const uint32_t& generation);
|
||||
|
||||
virtual bool
|
||||
DeallocPMemoryReportRequestChild(PMemoryReportRequestChild* actor);
|
||||
|
||||
virtual bool
|
||||
RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* child);
|
||||
RecvPMemoryReportRequestConstructor(PMemoryReportRequestChild* child,
|
||||
const uint32_t& generation);
|
||||
|
||||
virtual bool
|
||||
RecvAudioChannelNotify();
|
||||
|
@ -78,6 +78,7 @@
|
||||
#include "nsISupportsPrimitives.h"
|
||||
#include "nsIURIFixup.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#include "nsMemoryReporterManager.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsStyleSheetService.h"
|
||||
#include "nsThreadUtils.h"
|
||||
@ -160,61 +161,13 @@ namespace dom {
|
||||
|
||||
#define NS_IPC_IOSERVICE_SET_OFFLINE_TOPIC "ipc:network:set-offline"
|
||||
|
||||
// This represents all the memory reports provided by a child process.
|
||||
class ChildReporter MOZ_FINAL : public nsIMemoryReporter
|
||||
{
|
||||
public:
|
||||
ChildReporter(const InfallibleTArray<MemoryReport>& childReports)
|
||||
{
|
||||
for (uint32_t i = 0; i < childReports.Length(); i++) {
|
||||
MemoryReport r(childReports[i].process(),
|
||||
childReports[i].path(),
|
||||
childReports[i].kind(),
|
||||
childReports[i].units(),
|
||||
childReports[i].amount(),
|
||||
childReports[i].desc());
|
||||
|
||||
// Child reports have a non-empty process.
|
||||
MOZ_ASSERT(!r.process().IsEmpty());
|
||||
|
||||
mChildReports.AppendElement(r);
|
||||
}
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
NS_IMETHOD GetName(nsACString& name)
|
||||
{
|
||||
name.AssignLiteral("content-child");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCb,
|
||||
nsISupports* aClosure)
|
||||
{
|
||||
for (uint32_t i = 0; i < mChildReports.Length(); i++) {
|
||||
nsresult rv;
|
||||
MemoryReport r = mChildReports[i];
|
||||
rv = aCb->Callback(r.process(), r.path(), r.kind(), r.units(),
|
||||
r.amount(), r.desc(), aClosure);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
InfallibleTArray<MemoryReport> mChildReports;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(ChildReporter, nsIMemoryReporter)
|
||||
|
||||
class MemoryReportRequestParent : public PMemoryReportRequestParent
|
||||
{
|
||||
public:
|
||||
MemoryReportRequestParent();
|
||||
virtual ~MemoryReportRequestParent();
|
||||
|
||||
virtual bool Recv__delete__(const InfallibleTArray<MemoryReport>& report);
|
||||
virtual bool Recv__delete__(const uint32_t& generation, const InfallibleTArray<MemoryReport>& report);
|
||||
private:
|
||||
ContentParent* Owner()
|
||||
{
|
||||
@ -228,9 +181,13 @@ MemoryReportRequestParent::MemoryReportRequestParent()
|
||||
}
|
||||
|
||||
bool
|
||||
MemoryReportRequestParent::Recv__delete__(const InfallibleTArray<MemoryReport>& childReports)
|
||||
MemoryReportRequestParent::Recv__delete__(const uint32_t& generation, const InfallibleTArray<MemoryReport>& childReports)
|
||||
{
|
||||
Owner()->SetChildMemoryReports(childReports);
|
||||
nsRefPtr<nsMemoryReporterManager> mgr =
|
||||
nsMemoryReporterManager::GetOrCreate();
|
||||
if (mgr) {
|
||||
mgr->HandleChildReports(generation, childReports);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1043,7 +1000,6 @@ ContentParent::ShutDownProcess(bool aCloseWithError)
|
||||
// shut down the cycle collector. But by then it's too late to release any
|
||||
// CC'ed objects, so we need to null them out here, while we still can. See
|
||||
// bug 899761.
|
||||
mChildReporter = nullptr;
|
||||
if (mMessageManager) {
|
||||
mMessageManager->Disconnect();
|
||||
mMessageManager = nullptr;
|
||||
@ -1218,8 +1174,12 @@ ContentParent::ActorDestroy(ActorDestroyReason why)
|
||||
ppm->Disconnect();
|
||||
}
|
||||
|
||||
// unregister the child memory reporter
|
||||
UnregisterChildMemoryReporter();
|
||||
// Tell the memory reporter manager that this ContentParent is going away.
|
||||
nsRefPtr<nsMemoryReporterManager> mgr =
|
||||
nsMemoryReporterManager::GetOrCreate();
|
||||
if (mgr) {
|
||||
mgr->DecrementNumChildProcesses();
|
||||
}
|
||||
|
||||
// remove the global remote preferences observers
|
||||
Preferences::RemoveObserver(this, "");
|
||||
@ -1426,6 +1386,13 @@ ContentParent::ContentParent(mozIApplication* aApp,
|
||||
|
||||
IToplevelProtocol::SetTransport(mSubprocess->GetChannel());
|
||||
|
||||
// Tell the memory reporter manager that this ContentParent exists.
|
||||
nsRefPtr<nsMemoryReporterManager> mgr =
|
||||
nsMemoryReporterManager::GetOrCreate();
|
||||
if (mgr) {
|
||||
mgr->IncrementNumChildProcesses();
|
||||
}
|
||||
|
||||
std::vector<std::string> extraArgs;
|
||||
if (aIsNuwaProcess) {
|
||||
extraArgs.push_back("-nuwa");
|
||||
@ -2037,7 +2004,7 @@ ContentParent::Observe(nsISupports* aSubject,
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
else if (!strcmp(aTopic, "child-memory-reporter-request")) {
|
||||
unused << SendPMemoryReportRequestConstructor();
|
||||
unused << SendPMemoryReportRequestConstructor((uint32_t)(uintptr_t)aData);
|
||||
}
|
||||
else if (!strcmp(aTopic, "child-gc-request")){
|
||||
unused << SendGarbageCollect();
|
||||
@ -2481,7 +2448,7 @@ ContentParent::RecvPIndexedDBConstructor(PIndexedDBParent* aActor)
|
||||
}
|
||||
|
||||
PMemoryReportRequestParent*
|
||||
ContentParent::AllocPMemoryReportRequestParent()
|
||||
ContentParent::AllocPMemoryReportRequestParent(const uint32_t& generation)
|
||||
{
|
||||
MemoryReportRequestParent* parent = new MemoryReportRequestParent();
|
||||
return parent;
|
||||
@ -2494,32 +2461,6 @@ ContentParent::DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* act
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::SetChildMemoryReports(const InfallibleTArray<MemoryReport>& childReports)
|
||||
{
|
||||
nsCOMPtr<nsIMemoryReporterManager> mgr =
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
|
||||
if (mChildReporter)
|
||||
mgr->UnregisterReporter(mChildReporter);
|
||||
|
||||
mChildReporter = new ChildReporter(childReports);
|
||||
mgr->RegisterReporter(mChildReporter);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
if (obs)
|
||||
obs->NotifyObservers(nullptr, "child-memory-reporter-update", nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
ContentParent::UnregisterChildMemoryReporter()
|
||||
{
|
||||
nsCOMPtr<nsIMemoryReporterManager> mgr =
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
mgr->UnregisterReporter(mChildReporter);
|
||||
}
|
||||
|
||||
PTestShellParent*
|
||||
ContentParent::AllocPTestShellParent()
|
||||
{
|
||||
|
@ -140,10 +140,6 @@ public:
|
||||
bool IsAlive();
|
||||
bool IsForApp();
|
||||
|
||||
void SetChildMemoryReports(const InfallibleTArray<MemoryReport>&
|
||||
childReports);
|
||||
void UnregisterChildMemoryReporter();
|
||||
|
||||
GeckoChildProcessHost* Process() {
|
||||
return mSubprocess;
|
||||
}
|
||||
@ -340,7 +336,7 @@ private:
|
||||
|
||||
virtual bool DeallocPIndexedDBParent(PIndexedDBParent* aActor);
|
||||
|
||||
virtual PMemoryReportRequestParent* AllocPMemoryReportRequestParent();
|
||||
virtual PMemoryReportRequestParent* AllocPMemoryReportRequestParent(const uint32_t& generation);
|
||||
virtual bool DeallocPMemoryReportRequestParent(PMemoryReportRequestParent* actor);
|
||||
|
||||
virtual PTestShellParent* AllocPTestShellParent();
|
||||
@ -500,14 +496,6 @@ private:
|
||||
uint64_t mChildID;
|
||||
int32_t mGeolocationWatchID;
|
||||
|
||||
// This is a reporter holding the reports from the child's last
|
||||
// "child-memory-reporter-update" notification. To update this, one can
|
||||
// broadcast the topic "child-memory-reporter-request" using the
|
||||
// nsIObserverService.
|
||||
//
|
||||
// Note that this assumes there is at most one child process at a time!
|
||||
nsCOMPtr<nsIMemoryReporter> mChildReporter;
|
||||
|
||||
nsString mAppManifestURL;
|
||||
|
||||
/**
|
||||
|
@ -231,7 +231,7 @@ child:
|
||||
*/
|
||||
async SetProcessPrivileges(ChildPrivileges privs);
|
||||
|
||||
PMemoryReportRequest();
|
||||
PMemoryReportRequest(uint32_t generation);
|
||||
|
||||
/**
|
||||
* Notify the AudioChannelService in the child processes.
|
||||
|
@ -21,7 +21,7 @@ protocol PMemoryReportRequest {
|
||||
manager PContent;
|
||||
|
||||
parent:
|
||||
__delete__(MemoryReport[] report);
|
||||
__delete__(uint32_t generation, MemoryReport[] report);
|
||||
};
|
||||
|
||||
}
|
||||
|
@ -48,11 +48,6 @@ XPCOMUtils.defineLazyGetter(this, "nsGzipConverter",
|
||||
let gMgr = Cc["@mozilla.org/memory-reporter-manager;1"]
|
||||
.getService(Ci.nsIMemoryReporterManager);
|
||||
|
||||
// We need to know about "child-memory-reporter-update" events from child
|
||||
// processes.
|
||||
Services.obs.addObserver(updateAboutMemoryFromReporters,
|
||||
"child-memory-reporter-update", false);
|
||||
|
||||
let gUnnamedProcessStr = "Main Process";
|
||||
|
||||
let gIsDiff = false;
|
||||
@ -120,8 +115,6 @@ function debug(x)
|
||||
|
||||
function onUnload()
|
||||
{
|
||||
Services.obs.removeObserver(updateAboutMemoryFromReporters,
|
||||
"child-memory-reporter-update");
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@ -388,11 +381,6 @@ function doMMU()
|
||||
|
||||
function doMeasure()
|
||||
{
|
||||
// Notify any children that they should measure memory consumption, then
|
||||
// update the page. If any reports come back from children,
|
||||
// updateAboutMemoryFromReporters() will be called again and the page will
|
||||
// regenerate.
|
||||
Services.obs.notifyObservers(null, "child-memory-reporter-request", null);
|
||||
updateAboutMemoryFromReporters();
|
||||
}
|
||||
|
||||
@ -402,10 +390,7 @@ function doMeasure()
|
||||
*/
|
||||
function updateAboutMemoryFromReporters()
|
||||
{
|
||||
// First, clear the contents of main. Necessary because
|
||||
// updateAboutMemoryFromReporters() might be called more than once due to the
|
||||
// "child-memory-reporter-update" observer.
|
||||
updateMainAndFooter("", SHOW_FOOTER);
|
||||
updateMainAndFooter("Measuring...", HIDE_FOOTER);
|
||||
|
||||
try {
|
||||
let processLiveMemoryReports =
|
||||
@ -416,12 +401,13 @@ function updateAboutMemoryFromReporters()
|
||||
aDescription, /* presence = */ undefined);
|
||||
}
|
||||
|
||||
let e = gMgr.enumerateReporters();
|
||||
while (e.hasMoreElements()) {
|
||||
let mr = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
|
||||
mr.collectReports(handleReport, null);
|
||||
let displayReportsAndFooter = function() {
|
||||
updateMainAndFooter("", SHOW_FOOTER);
|
||||
aDisplayReports();
|
||||
}
|
||||
aDisplayReports();
|
||||
|
||||
gMgr.getReports(handleReport, null,
|
||||
displayReportsAndFooter, null);
|
||||
}
|
||||
|
||||
// Process the reports from the live memory reporters.
|
||||
@ -1841,9 +1827,11 @@ function saveReportsToFile()
|
||||
let dumper = Cc["@mozilla.org/memory-info-dumper;1"]
|
||||
.getService(Ci.nsIMemoryInfoDumper);
|
||||
|
||||
dumper.dumpMemoryReportsToNamedFile(fp.file.path);
|
||||
let finishDumping = () => {
|
||||
updateMainAndFooter("Saved reports to " + fp.file.path, HIDE_FOOTER);
|
||||
}
|
||||
|
||||
updateMainAndFooter("Saved reports to " + fp.file.path, HIDE_FOOTER);
|
||||
dumper.dumpMemoryReportsToNamedFile(fp.file.path, finishDumping, null);
|
||||
}
|
||||
};
|
||||
fp.open(fpCallback);
|
||||
|
@ -10,6 +10,7 @@ support-files =
|
||||
[test_aboutmemory2.xul]
|
||||
[test_aboutmemory3.xul]
|
||||
[test_aboutmemory4.xul]
|
||||
[test_aboutmemory5.xul]
|
||||
[test_memoryReporters.xul]
|
||||
[test_memoryReporters2.xul]
|
||||
[test_sqliteMultiReporter.xul]
|
||||
|
@ -108,21 +108,60 @@
|
||||
let e = document.createEvent('Event');
|
||||
e.initEvent('change', true, true);
|
||||
|
||||
if (!aFilename2) {
|
||||
if (aDumpFirst) {
|
||||
let dumper = Cc["@mozilla.org/memory-info-dumper;1"].
|
||||
getService(Ci.nsIMemoryInfoDumper);
|
||||
function check() {
|
||||
// Initialize the clipboard contents.
|
||||
SpecialPowers.clipboardCopyString("initial clipboard value");
|
||||
|
||||
dumper.dumpMemoryReportsToNamedFile(filePath,
|
||||
/* minimizeMemoryUsage = */ false,
|
||||
/* dumpChildProcesses = */ false);
|
||||
let numFailures = 0, maxFailures = 30;
|
||||
|
||||
// Because the file load is async, we don't know when it will finish and
|
||||
// the output will show up. So we poll.
|
||||
function copyPasteAndCheck() {
|
||||
// Copy and paste frame contents, and filter out non-deterministic
|
||||
// differences.
|
||||
synthesizeKey("A", {accelKey: true});
|
||||
synthesizeKey("C", {accelKey: true});
|
||||
let actual = SpecialPowers.getClipboardData("text/unicode");
|
||||
actual = actual.replace(/\(pid \d+\)/g, "(pid NNN)");
|
||||
|
||||
if (actual === aExpected) {
|
||||
SimpleTest.ok(true, "Clipboard has the expected contents");
|
||||
aNext();
|
||||
} else {
|
||||
numFailures++;
|
||||
if (numFailures === maxFailures) {
|
||||
ok(false, "pasted text doesn't match");
|
||||
dump("******EXPECTED******\n");
|
||||
dump(aExpected);
|
||||
dump("*******ACTUAL*******\n");
|
||||
dump(actual);
|
||||
dump("********************\n");
|
||||
finish();
|
||||
} else {
|
||||
setTimeout(copyPasteAndCheck, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
copyPasteAndCheck();
|
||||
}
|
||||
|
||||
if (!aFilename2) {
|
||||
function loadAndCheck() {
|
||||
let fileInput1 =
|
||||
frame.contentWindow.document.getElementById("fileInput1");
|
||||
fileInput1.value = filePath; // this works because it's a chrome test
|
||||
|
||||
fileInput1.dispatchEvent(e);
|
||||
check();
|
||||
}
|
||||
|
||||
let fileInput1 =
|
||||
frame.contentWindow.document.getElementById("fileInput1");
|
||||
fileInput1.value = filePath; // this works because it's a chrome test
|
||||
|
||||
fileInput1.dispatchEvent(e);
|
||||
if (aDumpFirst) {
|
||||
let dumper = Cc["@mozilla.org/memory-info-dumper;1"].
|
||||
getService(Ci.nsIMemoryInfoDumper);
|
||||
dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null);
|
||||
} else {
|
||||
loadAndCheck();
|
||||
}
|
||||
|
||||
} else {
|
||||
let fileInput2 =
|
||||
@ -144,42 +183,9 @@
|
||||
let e2 = document.createEvent('Event');
|
||||
e2.initEvent('change', true, true);
|
||||
fileInput2.dispatchEvent(e);
|
||||
|
||||
check();
|
||||
}
|
||||
|
||||
// Initialize the clipboard contents.
|
||||
SpecialPowers.clipboardCopyString("initial clipboard value");
|
||||
|
||||
let numFailures = 0, maxFailures = 30;
|
||||
|
||||
// Because the file load is async, we don't know when it will finish and
|
||||
// the output will show up. So we poll.
|
||||
function copyPasteAndCheck() {
|
||||
// Copy and paste frame contents, and filter out non-deterministic
|
||||
// differences.
|
||||
synthesizeKey("A", {accelKey: true});
|
||||
synthesizeKey("C", {accelKey: true});
|
||||
let actual = SpecialPowers.getClipboardData("text/unicode");
|
||||
actual = actual.replace(/\(pid \d+\)/g, "(pid NNN)");
|
||||
|
||||
if (actual === aExpected) {
|
||||
SimpleTest.ok(true, "Clipboard has the expected contents");
|
||||
aNext();
|
||||
} else {
|
||||
numFailures++;
|
||||
if (numFailures === maxFailures) {
|
||||
ok(false, "pasted text doesn't match");
|
||||
dump("******EXPECTED******\n");
|
||||
dump(aExpected);
|
||||
dump("*******ACTUAL*******\n");
|
||||
dump(actual);
|
||||
dump("********************\n");
|
||||
finish();
|
||||
} else {
|
||||
setTimeout(copyPasteAndCheck, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
copyPasteAndCheck();
|
||||
}
|
||||
|
||||
// Returns a function that chains together multiple test() calls.
|
||||
|
142
toolkit/components/aboutmemory/tests/test_aboutmemory5.xul
Normal file
142
toolkit/components/aboutmemory/tests/test_aboutmemory5.xul
Normal file
@ -0,0 +1,142 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<window title="about:memory"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<!-- This file tests the saving and loading of memory reports to/from file in
|
||||
about:memory in the presence of child processes. -->
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml"></body>
|
||||
|
||||
<iframe id="amFrame" height="400" src="about:memory"></iframe>
|
||||
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
"use strict";
|
||||
|
||||
const Cc = Components.classes;
|
||||
const Ci = Components.interfaces;
|
||||
let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
|
||||
let numRemotes = 3;
|
||||
let numReady = 0;
|
||||
|
||||
// Create some remote processes, and set up message-passing so that
|
||||
// we know when each child is fully initialized.
|
||||
let remotes = [];
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 3]]}, function() {
|
||||
for (let i = 0; i < numRemotes; i++) {
|
||||
let w = remotes[i] = window.open("remote.xul", "", "chrome");
|
||||
|
||||
w.addEventListener("load", function loadHandler() {
|
||||
w.removeEventListener("load", loadHandler);
|
||||
let remoteBrowser = w.document.getElementById("remote");
|
||||
let mm = remoteBrowser.messageManager;
|
||||
mm.addMessageListener("test:ready", function readyHandler() {
|
||||
mm.removeMessageListener("test:ready", readyHandler);
|
||||
numReady++;
|
||||
if (numReady == numRemotes) {
|
||||
// All the remote processes are ready.
|
||||
SimpleTest.waitForFocus(onFocus);
|
||||
}
|
||||
});
|
||||
mm.loadFrameScript("data:," + encodeURI("sendAsyncMessage('test:ready');"), true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Load the given file into the frame, then copy+paste the entire frame and
|
||||
// check that the cut text matches what we expect.
|
||||
function onFocus() {
|
||||
let frame = document.getElementById("amFrame");
|
||||
frame.focus();
|
||||
|
||||
function getFilePath(aFilename) {
|
||||
let file = Cc["@mozilla.org/file/directory_service;1"]
|
||||
.getService(Components.interfaces.nsIProperties)
|
||||
.get("CurWorkD", Components.interfaces.nsIFile);
|
||||
file.append("chrome");
|
||||
file.append("toolkit");
|
||||
file.append("components");
|
||||
file.append("aboutmemory");
|
||||
file.append("tests");
|
||||
file.append(aFilename);
|
||||
return file.path;
|
||||
}
|
||||
|
||||
let filePath = getFilePath("memory-reports-dumped.json.gz");
|
||||
|
||||
let e = document.createEvent('Event');
|
||||
e.initEvent('change', true, true);
|
||||
|
||||
let dumper = Cc["@mozilla.org/memory-info-dumper;1"].
|
||||
getService(Ci.nsIMemoryInfoDumper);
|
||||
dumper.dumpMemoryReportsToNamedFile(filePath, loadAndCheck, null);
|
||||
|
||||
function loadAndCheck() {
|
||||
// Load the file.
|
||||
let fileInput1 =
|
||||
frame.contentWindow.document.getElementById("fileInput1");
|
||||
fileInput1.value = filePath; // this works because it's a chrome test
|
||||
fileInput1.dispatchEvent(e);
|
||||
|
||||
// Initialize the clipboard contents.
|
||||
SpecialPowers.clipboardCopyString("initial clipboard value");
|
||||
|
||||
let numFailures = 0, maxFailures = 30;
|
||||
|
||||
copyPasteAndCheck();
|
||||
|
||||
// Because the file load is async, we don't know when it will finish and
|
||||
// the output will show up. So we poll.
|
||||
function copyPasteAndCheck() {
|
||||
// Copy and paste frame contents, and filter out non-deterministic
|
||||
// differences.
|
||||
synthesizeKey("A", {accelKey: true});
|
||||
synthesizeKey("C", {accelKey: true});
|
||||
let actual = SpecialPowers.getClipboardData("text/unicode");
|
||||
|
||||
// If we have more than 1000 chars, we've probably successfully
|
||||
// copy+pasted.
|
||||
if (actual.length > 1000) {
|
||||
let vsizes = actual.match(/vsize/g);
|
||||
let endOfBrowsers = actual.match(/End of Browser/g);
|
||||
|
||||
if (vsizes.length == 4 && endOfBrowsers.length == 3) {
|
||||
ok(true, "three child processes present in loaded data");
|
||||
} else {
|
||||
ok(false, "pasted text lacks four 'vsize' and three 'End of Browser' strings");
|
||||
dump("*******ACTUAL*******\n");
|
||||
dump(actual);
|
||||
dump("********************\n");
|
||||
}
|
||||
|
||||
// Close the remote processes.
|
||||
for (let i = 0; i < numRemotes; i++) {
|
||||
remotes[i].close();
|
||||
}
|
||||
|
||||
SimpleTest.finish();
|
||||
|
||||
} else {
|
||||
numFailures++;
|
||||
if (numFailures === maxFailures) {
|
||||
ok(false, "not enough chars in pasted output");
|
||||
SimpleTest.finish();
|
||||
} else {
|
||||
setTimeout(copyPasteAndCheck, 100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
@ -19,15 +19,11 @@
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// We want to know when a remote process sends us an update.
|
||||
let os = Cc["@mozilla.org/observer-service;1"]
|
||||
.getService(Ci.nsIObserverService);
|
||||
os.addObserver(childMemoryReporterUpdate, "child-memory-reporter-update", false);
|
||||
|
||||
let numRemotes = 3;
|
||||
let numReady = 0;
|
||||
|
||||
// Create some remote processes, and set up message-passing so that go() is
|
||||
// called once per process, once it is fully initialized.
|
||||
// Create some remote processes, and set up message-passing so that
|
||||
// we know when each child is fully initialized.
|
||||
let remotes = [];
|
||||
SpecialPowers.pushPrefEnv({"set": [["dom.ipc.processCount", 3]]}, function() {
|
||||
for (let i = 0; i < numRemotes; i++) {
|
||||
@ -39,47 +35,40 @@
|
||||
let mm = remoteBrowser.messageManager;
|
||||
mm.addMessageListener("test:ready", function readyHandler() {
|
||||
mm.removeMessageListener("test:ready", readyHandler);
|
||||
remoteReady();
|
||||
numReady++;
|
||||
if (numReady == numRemotes) {
|
||||
// All the remote processes are ready. Do memory reporting.
|
||||
doReports();
|
||||
}
|
||||
});
|
||||
mm.loadFrameScript("data:," + encodeURI("sendAsyncMessage('test:ready');"), true);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
let numReady = 0;
|
||||
let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
|
||||
function remoteReady()
|
||||
function doReports()
|
||||
{
|
||||
numReady++;
|
||||
if (numReady == numRemotes) {
|
||||
// All the remote processes are ready. Ask them for memory reports.
|
||||
os.notifyObservers(null, "child-memory-reporter-request", null);
|
||||
}
|
||||
}
|
||||
let residents = {};
|
||||
|
||||
let numUpdates = 0;
|
||||
|
||||
function childMemoryReporterUpdate()
|
||||
{
|
||||
numUpdates++;
|
||||
if (numUpdates == numRemotes) {
|
||||
// All the remote processes have reported back. Check reports.
|
||||
let residents = {};
|
||||
|
||||
// Get all the reports.
|
||||
let mgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
||||
getService(Ci.nsIMemoryReporterManager);
|
||||
let e = mgr.enumerateReporters();
|
||||
while (e.hasMoreElements()) {
|
||||
let r = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
|
||||
r.collectReports(function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
|
||||
if (aPath === "resident") {
|
||||
ok(100 * 1000 <= aAmount && aAmount <= 10 * 1000 * 1000 * 1000,
|
||||
"resident is reasonable");
|
||||
residents[aProcess] = aAmount;
|
||||
}
|
||||
}, null);
|
||||
let handleReport = function(aProcess, aPath, aKind, aUnits, aAmount, aDesc) {
|
||||
if (aPath === "resident") {
|
||||
ok(100 * 1000 <= aAmount && aAmount <= 10 * 1000 * 1000 * 1000,
|
||||
"resident is reasonable");
|
||||
residents[aProcess] = aAmount;
|
||||
}
|
||||
}
|
||||
|
||||
let processReports = function() {
|
||||
// First, test a failure case: calling getReports() before the previous
|
||||
// getReports() has finished should silently abort. (And the arguments
|
||||
// won't be used.)
|
||||
mgr.getReports(
|
||||
() => ok(false, "handleReport called for nested getReports() call"),
|
||||
null, null, null
|
||||
);
|
||||
|
||||
// Close the remote processes.
|
||||
for (let i = 0; i < numRemotes; i++) {
|
||||
@ -107,6 +96,9 @@
|
||||
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
mgr.getReports(handleReport, null, processReports, null);
|
||||
}
|
||||
|
||||
]]></script>
|
||||
</window>
|
||||
|
@ -5,13 +5,18 @@
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
[scriptable, builtinclass, uuid(3FFA5113-2A10-43BB-923B-6A2FAF67BE97)]
|
||||
[scriptable, function, uuid(2dea18fc-fbfa-4bf7-ad45-0efaf5495f5e)]
|
||||
interface nsIFinishDumpingCallback : nsISupports
|
||||
{
|
||||
void callback(in nsISupports data);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(fd5de30a-e3d6-4965-8244-86709e1ed23b)]
|
||||
interface nsIMemoryInfoDumper : nsISupports
|
||||
{
|
||||
/**
|
||||
* This dumps gzipped memory reports for this process. If a file of the
|
||||
* given name exists, it will be overwritten. Nothing is done for any child
|
||||
* processes (and their children, recursively).
|
||||
* This dumps gzipped memory reports for this process and its child
|
||||
* processes. If a file of the given name exists, it will be overwritten.
|
||||
*
|
||||
* @param aFilename The output file.
|
||||
*
|
||||
@ -83,17 +88,19 @@ interface nsIMemoryInfoDumper : nsISupports
|
||||
* }
|
||||
* }
|
||||
*/
|
||||
void dumpMemoryReportsToNamedFile(in AString aFilename);
|
||||
void dumpMemoryReportsToNamedFile(in AString aFilename,
|
||||
in nsIFinishDumpingCallback aFinishDumping,
|
||||
in nsISupports aFinishDumpingData);
|
||||
|
||||
/**
|
||||
* Similar to dumpMemoryReportsToNamedFile, this method dumps gzipped memory
|
||||
* reports for this process and possibly our child processes (and their
|
||||
* reports for this process and possibly its child processes (and their
|
||||
* children, recursively) to a file in the tmp directory called
|
||||
* memory-reports-<identifier>-<pid>.json.gz (or something similar, such as
|
||||
* memory-reports-<identifier>-<pid>-1.json.gz; no existing file will be
|
||||
* overwritten).
|
||||
*
|
||||
* If DMD is enabled, this method also dump gzipped DMD output to a file in
|
||||
* If DMD is enabled, this method also dumps gzipped DMD output to a file in
|
||||
* the tmp directory called dmd-<identifier>-<pid>.txt.gz (or something
|
||||
* similar; again, no existing file will be overwritten).
|
||||
*
|
||||
|
@ -179,13 +179,19 @@ interface nsIMemoryReporter : nsISupports
|
||||
const int32_t UNITS_PERCENTAGE = 3;
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(4db7040a-16f9-4249-879b-fe72729c7ef5)]
|
||||
[scriptable, function, uuid(548b3909-c04d-4ca6-8466-b8bee3837457)]
|
||||
interface nsIFinishReportingCallback : nsISupports
|
||||
{
|
||||
void callback(in nsISupports data);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(a1292276-726b-4ef5-a017-5a455d6664dd)]
|
||||
interface nsIMemoryReporterManager : nsISupports
|
||||
{
|
||||
/*
|
||||
* Return an enumerator of nsIMemoryReporters that are currently registered.
|
||||
* Initialize.
|
||||
*/
|
||||
nsISimpleEnumerator enumerateReporters();
|
||||
void init();
|
||||
|
||||
/*
|
||||
* Register the given nsIMemoryReporter. After a reporter is registered,
|
||||
@ -207,9 +213,30 @@ interface nsIMemoryReporterManager : nsISupports
|
||||
void registerReporterEvenIfBlocked(in nsIMemoryReporter aReporter);
|
||||
|
||||
/*
|
||||
* Initialize.
|
||||
* Return an enumerator of nsIMemoryReporters that are currently registered
|
||||
* in the current process. WARNING: this does not do anything with child
|
||||
* processes. Use getReports() if you want measurements from child
|
||||
* processes.
|
||||
*/
|
||||
void init();
|
||||
nsISimpleEnumerator enumerateReporters();
|
||||
|
||||
/*
|
||||
* Get memory reports for the current process and all child processes.
|
||||
* |handleReport| is called for each report, and |finishReporting| is called
|
||||
* once all reports have been handled.
|
||||
*
|
||||
* |finishReporting| is called even if, for example, some child processes
|
||||
* fail to report back. However, calls to this method will silently and
|
||||
* immediately abort -- and |finishReporting| will not be called -- if a
|
||||
* previous getReports() call is still in flight, i.e. if it has not yet
|
||||
* finished invoking |finishReporting|. The silent abort is because the
|
||||
* in-flight request will finish soon, and the caller would very likely just
|
||||
* catch and ignore any error anyway.
|
||||
*/
|
||||
void getReports(in nsIMemoryReporterCallback handleReport,
|
||||
in nsISupports handleReportData,
|
||||
in nsIFinishReportingCallback finishReporting,
|
||||
in nsISupports finishReportingData);
|
||||
|
||||
/*
|
||||
* The memory reporter manager, for the most part, treats reporters
|
||||
@ -332,9 +359,14 @@ interface nsIMemoryReporterManager : nsISupports
|
||||
|
||||
#include "js/TypeDecls.h"
|
||||
#include "nsStringGlue.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsPIDOMWindow;
|
||||
|
||||
// nsIHandleReportCallback is a better name, but keep nsIMemoryReporterCallback
|
||||
// around for backwards compatibility.
|
||||
typedef nsIMemoryReporterCallback nsIHandleReportCallback;
|
||||
|
||||
// Note that the memory reporters are held in an nsCOMArray, which means
|
||||
// that individual reporters should be referenced with |nsIMemoryReporter *|
|
||||
// instead of nsCOMPtr<nsIMemoryReporter>.
|
||||
@ -396,8 +428,9 @@ nsresult RegisterNonJSSizeOfTab(NonJSSizeOfTabFn aSizeOfTabFn);
|
||||
#if defined(MOZ_DMD)
|
||||
namespace mozilla {
|
||||
namespace dmd {
|
||||
// This runs all the memory reporters but does nothing with the results; i.e.
|
||||
// it does the minimal amount of work possible for DMD to do its thing.
|
||||
// This runs all the memory reporters in the current process but does nothing
|
||||
// with the results; i.e. it does the minimal amount of work possible for DMD
|
||||
// to do its thing. It does nothing with child processes.
|
||||
void RunReporters();
|
||||
}
|
||||
}
|
||||
|
@ -610,32 +610,33 @@ namespace mozilla {
|
||||
} while (0)
|
||||
|
||||
static nsresult
|
||||
DumpReport(nsIGZFileWriter *aWriter, bool *aIsFirstPtr,
|
||||
DumpReport(nsIGZFileWriter *aWriter, bool aIsFirst,
|
||||
const nsACString &aProcess, const nsACString &aPath, int32_t aKind,
|
||||
int32_t aUnits, int64_t aAmount, const nsACString &aDescription)
|
||||
{
|
||||
// We only want to dump reports for this process. If |aProcess| is
|
||||
// non-nullptr that means we've received it from another process in response
|
||||
// to a "child-memory-reporter-request" event; ignore such reports.
|
||||
if (!aProcess.IsEmpty()) {
|
||||
return NS_OK;
|
||||
}
|
||||
DUMP(aWriter, aIsFirst ? "[" : ",");
|
||||
|
||||
DUMP(aWriter, *aIsFirstPtr ? "[" : ",");
|
||||
*aIsFirstPtr = false;
|
||||
|
||||
// Generate the process identifier, which is of the form "$PROCESS_NAME
|
||||
// (pid $PID)", or just "(pid $PID)" if we don't have a process name. If
|
||||
// we're the main process, we let $PROCESS_NAME be "Main Process".
|
||||
nsAutoCString process;
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
// We're the main process.
|
||||
process.AssignLiteral("Main Process ");
|
||||
} else if (ContentChild *cc = ContentChild::GetSingleton()) {
|
||||
// Try to get the process name from ContentChild.
|
||||
cc->GetProcessName(process);
|
||||
if (aProcess.IsEmpty()) {
|
||||
// If the process is empty, the report originated with the process doing
|
||||
// the dumping. In that case, generate the process identifier, which is of
|
||||
// the form "$PROCESS_NAME (pid $PID)", or just "(pid $PID)" if we don't
|
||||
// have a process name. If we're the main process, we let $PROCESS_NAME be
|
||||
// "Main Process".
|
||||
if (XRE_GetProcessType() == GeckoProcessType_Default) {
|
||||
// We're the main process.
|
||||
process.AssignLiteral("Main Process");
|
||||
} else if (ContentChild *cc = ContentChild::GetSingleton()) {
|
||||
// Try to get the process name from ContentChild.
|
||||
cc->GetProcessName(process);
|
||||
}
|
||||
ContentChild::AppendProcessId(process);
|
||||
|
||||
} else {
|
||||
// Otherwise, the report originated with another process and already has a
|
||||
// process name. Just use that.
|
||||
process = aProcess;
|
||||
}
|
||||
ContentChild::AppendProcessId(process);
|
||||
|
||||
DUMP(aWriter, "\n {\"process\": \"");
|
||||
DUMP(aWriter, process);
|
||||
@ -666,12 +667,12 @@ DumpReport(nsIGZFileWriter *aWriter, bool *aIsFirstPtr,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
class DumpReporterCallback MOZ_FINAL : public nsIMemoryReporterCallback
|
||||
class DumpReportCallback MOZ_FINAL : public nsIHandleReportCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
DumpReporterCallback() : mIsFirst(true) {}
|
||||
DumpReportCallback() : mIsFirst(true) {}
|
||||
|
||||
NS_IMETHOD Callback(const nsACString &aProcess, const nsACString &aPath,
|
||||
int32_t aKind, int32_t aUnits, int64_t aAmount,
|
||||
@ -681,15 +682,17 @@ public:
|
||||
nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
|
||||
NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
|
||||
|
||||
return DumpReport(writer, &mIsFirst, aProcess, aPath, aKind, aUnits,
|
||||
aAmount, aDescription);
|
||||
nsresult rv = DumpReport(writer, mIsFirst, aProcess, aPath, aKind, aUnits,
|
||||
aAmount, aDescription);
|
||||
mIsFirst = false;
|
||||
return rv;
|
||||
}
|
||||
|
||||
private:
|
||||
bool mIsFirst;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(DumpReporterCallback, nsIMemoryReporterCallback)
|
||||
NS_IMPL_ISUPPORTS1(DumpReportCallback, nsIHandleReportCallback)
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@ -785,7 +788,7 @@ DMDWrite(void* aState, const char* aFmt, va_list ap)
|
||||
#endif
|
||||
|
||||
static nsresult
|
||||
DumpProcessMemoryReportsToGZFileWriter(nsIGZFileWriter *aWriter)
|
||||
DumpHeader(nsIGZFileWriter* aWriter)
|
||||
{
|
||||
// Increment this number if the format changes.
|
||||
//
|
||||
@ -804,22 +807,39 @@ DumpProcessMemoryReportsToGZFileWriter(nsIGZFileWriter *aWriter)
|
||||
DUMP(aWriter, ",\n");
|
||||
DUMP(aWriter, " \"reports\": ");
|
||||
|
||||
// Process reporters.
|
||||
bool more;
|
||||
nsCOMPtr<nsISimpleEnumerator> e;
|
||||
mgr->EnumerateReporters(getter_AddRefs(e));
|
||||
nsRefPtr<DumpReporterCallback> cb = new DumpReporterCallback();
|
||||
while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
|
||||
nsCOMPtr<nsIMemoryReporter> r;
|
||||
e->GetNext(getter_AddRefs(r));
|
||||
r->CollectReports(cb, aWriter);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
DumpFooter(nsIGZFileWriter* aWriter)
|
||||
{
|
||||
DUMP(aWriter, "\n ]\n}\n");
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
DumpProcessMemoryReportsToGZFileWriter(nsIGZFileWriter* aWriter)
|
||||
{
|
||||
nsresult rv = DumpHeader(aWriter);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
// Process reporters.
|
||||
bool more;
|
||||
nsCOMPtr<nsISimpleEnumerator> e;
|
||||
nsCOMPtr<nsIMemoryReporterManager> mgr =
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
mgr->EnumerateReporters(getter_AddRefs(e));
|
||||
nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback();
|
||||
while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
|
||||
nsCOMPtr<nsIMemoryReporter> r;
|
||||
e->GetNext(getter_AddRefs(r));
|
||||
r->CollectReports(dumpReport, aWriter);
|
||||
}
|
||||
|
||||
return DumpFooter(aWriter);
|
||||
}
|
||||
|
||||
nsresult
|
||||
DumpProcessMemoryInfoToTempDir(const nsAString& aIdentifier)
|
||||
{
|
||||
@ -977,8 +997,45 @@ nsMemoryInfoDumper::DumpMemoryInfoToTempDir(const nsAString& aIdentifier,
|
||||
return DumpProcessMemoryInfoToTempDir(identifier);
|
||||
}
|
||||
|
||||
// This dumps the JSON footer and closes the file, and then calls the given
|
||||
// nsIFinishDumpingCallback.
|
||||
class FinishReportingCallback MOZ_FINAL : public nsIFinishReportingCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
FinishReportingCallback(nsIFinishDumpingCallback* aFinishDumping,
|
||||
nsISupports* aFinishDumpingData)
|
||||
: mFinishDumping(aFinishDumping)
|
||||
, mFinishDumpingData(aFinishDumpingData)
|
||||
{}
|
||||
|
||||
NS_IMETHOD Callback(nsISupports* aData)
|
||||
{
|
||||
nsCOMPtr<nsIGZFileWriter> writer = do_QueryInterface(aData);
|
||||
NS_ENSURE_TRUE(writer, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = DumpFooter(writer);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
rv = writer->Finish();
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return mFinishDumping->Callback(mFinishDumpingData);
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIFinishDumpingCallback> mFinishDumping;
|
||||
nsCOMPtr<nsISupports> mFinishDumpingData;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS1(FinishReportingCallback, nsIFinishReportingCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(const nsAString& aFilename)
|
||||
nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(
|
||||
const nsAString& aFilename,
|
||||
nsIFinishDumpingCallback* aFinishDumping,
|
||||
nsISupports* aFinishDumpingData)
|
||||
{
|
||||
MOZ_ASSERT(!aFilename.IsEmpty());
|
||||
|
||||
@ -1006,12 +1063,16 @@ nsMemoryInfoDumper::DumpMemoryReportsToNamedFile(const nsAString& aFilename)
|
||||
rv = mrWriter->Init(mrFile);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
DumpProcessMemoryReportsToGZFileWriter(mrWriter);
|
||||
|
||||
rv = mrWriter->Finish();
|
||||
rv = DumpHeader(mrWriter);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
// Process reports and finish up.
|
||||
nsRefPtr<DumpReportCallback> dumpReport = new DumpReportCallback();
|
||||
nsRefPtr<FinishReportingCallback> finishReporting =
|
||||
new FinishReportingCallback(aFinishDumping, aFinishDumpingData);
|
||||
nsCOMPtr<nsIMemoryReporterManager> mgr =
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
return mgr->GetReports(dumpReport, mrWriter, finishReporting, mrWriter);
|
||||
}
|
||||
|
||||
#undef DUMP
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsMemoryReporterManager.h"
|
||||
#include "nsISimpleEnumerator.h"
|
||||
#include "nsITimer.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
@ -23,6 +24,7 @@
|
||||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "mozilla/dom/PMemoryReportRequestParent.h" // for dom::MemoryReport
|
||||
|
||||
#ifndef XP_WIN
|
||||
#include <unistd.h>
|
||||
@ -679,7 +681,7 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD CollectReports(nsIMemoryReporterCallback* aCallback,
|
||||
NS_IMETHOD CollectReports(nsIHandleReport* aHandleReport,
|
||||
nsISupports* aData)
|
||||
{
|
||||
dmd::Sizes sizes;
|
||||
@ -688,10 +690,10 @@ public:
|
||||
#define REPORT(_path, _amount, _desc) \
|
||||
do { \
|
||||
nsresult rv; \
|
||||
rv = aCallback->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
|
||||
nsIMemoryReporter::KIND_HEAP, \
|
||||
nsIMemoryReporter::UNITS_BYTES, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), aData); \
|
||||
rv = aHandleReport->Callback(EmptyCString(), NS_LITERAL_CSTRING(_path), \
|
||||
nsIMemoryReporter::KIND_HEAP, \
|
||||
nsIMemoryReporter::UNITS_BYTES, _amount, \
|
||||
NS_LITERAL_CSTRING(_desc), aData); \
|
||||
NS_ENSURE_SUCCESS(rv, rv); \
|
||||
} while (0)
|
||||
|
||||
@ -845,10 +847,11 @@ HashtableEnumerator::GetNext(nsISupports** aNext)
|
||||
|
||||
nsMemoryReporterManager::nsMemoryReporterManager()
|
||||
: mMutex("nsMemoryReporterManager::mMutex"),
|
||||
mIsRegistrationBlocked(false)
|
||||
mIsRegistrationBlocked(false),
|
||||
mNumChildProcesses(0),
|
||||
mNextGeneration(1),
|
||||
mGetReportsState(nullptr)
|
||||
{
|
||||
PodZero(&mAmountFns);
|
||||
PodZero(&mSizeOfTabFns);
|
||||
}
|
||||
|
||||
nsMemoryReporterManager::~nsMemoryReporterManager()
|
||||
@ -858,8 +861,8 @@ nsMemoryReporterManager::~nsMemoryReporterManager()
|
||||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult)
|
||||
{
|
||||
// Memory reporters are not necessarily threadsafe, so EnumerateReporters()
|
||||
// must be called from the main thread.
|
||||
// Memory reporters are not necessarily threadsafe, so this function must
|
||||
// be called from the main thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
@ -872,6 +875,220 @@ nsMemoryReporterManager::EnumerateReporters(nsISimpleEnumerator** aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
//#define DEBUG_CHILD_PROCESS_MEMORY_REPORTING 1
|
||||
|
||||
#ifdef DEBUG_CHILD_PROCESS_MEMORY_REPORTING
|
||||
#define MEMORY_REPORTING_LOG(format, ...) \
|
||||
fprintf(stderr, "++++ MEMORY REPORTING: " format, ##__VA_ARGS__);
|
||||
#else
|
||||
#define MEMORY_REPORTING_LOG(...)
|
||||
#endif
|
||||
|
||||
void
|
||||
nsMemoryReporterManager::IncrementNumChildProcesses()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
mNumChildProcesses++;
|
||||
MEMORY_REPORTING_LOG("IncrementNumChildProcesses --> %d\n",
|
||||
mNumChildProcesses);
|
||||
}
|
||||
|
||||
void
|
||||
nsMemoryReporterManager::DecrementNumChildProcesses()
|
||||
{
|
||||
if (!NS_IsMainThread()) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
MOZ_ASSERT(mNumChildProcesses > 0);
|
||||
mNumChildProcesses--;
|
||||
MEMORY_REPORTING_LOG("DecrementNumChildProcesses --> %d\n",
|
||||
mNumChildProcesses);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::GetReports(
|
||||
nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aHandleReportData,
|
||||
nsIFinishReportingCallback* aFinishReporting,
|
||||
nsISupports* aFinishReportingData)
|
||||
{
|
||||
// Memory reporters are not necessarily threadsafe, so this function must
|
||||
// be called from the main thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
uint32_t generation = mNextGeneration++;
|
||||
|
||||
if (mGetReportsState) {
|
||||
// A request is in flight. Don't start another one. And don't report
|
||||
// an error; just ignore it, and let the in-flight request finish.
|
||||
MEMORY_REPORTING_LOG("GetReports (gen=%u, s->gen=%u): abort\n",
|
||||
generation, mGetReportsState->mGeneration);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MEMORY_REPORTING_LOG("GetReports (gen=%u, %d child(ren) present)\n",
|
||||
generation, mNumChildProcesses);
|
||||
|
||||
if (mNumChildProcesses > 0) {
|
||||
// Request memory reports from child processes. We do this *before*
|
||||
// collecting reports for this process so each process can collect
|
||||
// reports in parallel.
|
||||
nsCOMPtr<nsIObserverService> obs =
|
||||
do_GetService("@mozilla.org/observer-service;1");
|
||||
NS_ENSURE_STATE(obs);
|
||||
|
||||
// Casting the uint32_t generation to |const PRUnichar*| is a hack, but
|
||||
// simpler than converting the number to an actual string.
|
||||
obs->NotifyObservers(nullptr, "child-memory-reporter-request",
|
||||
(const PRUnichar*)(uintptr_t)generation);
|
||||
|
||||
nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
|
||||
NS_ENSURE_TRUE(timer, NS_ERROR_FAILURE);
|
||||
nsresult rv = timer->InitWithFuncCallback(TimeoutCallback,
|
||||
this, kTimeoutLengthMS,
|
||||
nsITimer::TYPE_ONE_SHOT);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mGetReportsState = new GetReportsState(generation,
|
||||
timer,
|
||||
mNumChildProcesses,
|
||||
aHandleReport,
|
||||
aHandleReportData,
|
||||
aFinishReporting,
|
||||
aFinishReportingData);
|
||||
}
|
||||
|
||||
// Get reports for this process.
|
||||
nsRefPtr<HashtableEnumerator> e;
|
||||
{
|
||||
mozilla::MutexAutoLock autoLock(mMutex);
|
||||
e = new HashtableEnumerator(mReporters);
|
||||
}
|
||||
bool more;
|
||||
while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
|
||||
nsCOMPtr<nsIMemoryReporter> r;
|
||||
e->GetNext(getter_AddRefs(r));
|
||||
r->CollectReports(aHandleReport, aHandleReportData);
|
||||
}
|
||||
|
||||
// If there are no child processes, we can finish up immediately.
|
||||
return (mNumChildProcesses == 0)
|
||||
? aFinishReporting->Callback(aFinishReportingData)
|
||||
: NS_OK;
|
||||
}
|
||||
|
||||
// This function has no return value. If something goes wrong, there's no
|
||||
// clear place to report the problem to, but that's ok -- we will end up
|
||||
// hitting the timeout and executing TimeoutCallback().
|
||||
void
|
||||
nsMemoryReporterManager::HandleChildReports(
|
||||
const uint32_t& aGeneration,
|
||||
const InfallibleTArray<dom::MemoryReport>& aChildReports)
|
||||
{
|
||||
// Memory reporting only happens on the main thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
GetReportsState* s = mGetReportsState;
|
||||
|
||||
if (!s) {
|
||||
// If we reach here, either:
|
||||
//
|
||||
// - A child process reported back too late, and no subsequent request
|
||||
// is in flight.
|
||||
//
|
||||
// - (Unlikely) A "child-memory-reporter-request" notification was
|
||||
// triggered from somewhere other than GetReports(), causing child
|
||||
// processes to report back when the nsMemoryReporterManager wasn't
|
||||
// expecting it.
|
||||
//
|
||||
// Either way, there's nothing to be done. Just ignore it.
|
||||
MEMORY_REPORTING_LOG(
|
||||
"HandleChildReports: no request in flight (aGen=%u)\n",
|
||||
aGeneration);
|
||||
return;
|
||||
}
|
||||
|
||||
if (aGeneration != s->mGeneration) {
|
||||
// If we reach here, a child process must have reported back, too late,
|
||||
// while a subsequent (higher-numbered) request is in flight. Again,
|
||||
// ignore it.
|
||||
MOZ_ASSERT(aGeneration < s->mGeneration);
|
||||
MEMORY_REPORTING_LOG(
|
||||
"HandleChildReports: gen mismatch (aGen=%u, s->gen=%u)\n",
|
||||
aGeneration, s->mGeneration);
|
||||
return;
|
||||
}
|
||||
|
||||
// Process the reports from the child process.
|
||||
for (uint32_t i = 0; i < aChildReports.Length(); i++) {
|
||||
const dom::MemoryReport& r = aChildReports[i];
|
||||
|
||||
// Child reports should have a non-empty process.
|
||||
MOZ_ASSERT(!r.process().IsEmpty());
|
||||
|
||||
// If the call fails, ignore and continue.
|
||||
s->mHandleReport->Callback(r.process(), r.path(), r.kind(),
|
||||
r.units(), r.amount(), r.desc(),
|
||||
s->mHandleReportData);
|
||||
}
|
||||
|
||||
// If all the child processes have reported, we can cancel the timer and
|
||||
// finish up. Otherwise, just return.
|
||||
|
||||
s->mNumChildProcessesCompleted++;
|
||||
MEMORY_REPORTING_LOG("HandleChildReports (aGen=%u): completed child %d\n",
|
||||
aGeneration, s->mNumChildProcessesCompleted);
|
||||
|
||||
if (s->mNumChildProcessesCompleted == s->mNumChildProcesses) {
|
||||
s->mTimer->Cancel();
|
||||
FinishReporting();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsMemoryReporterManager::TimeoutCallback(nsITimer* aTimer, void* aData)
|
||||
{
|
||||
nsMemoryReporterManager* mgr =
|
||||
static_cast<nsMemoryReporterManager*>(aData);
|
||||
|
||||
MOZ_ASSERT(mgr->mGetReportsState);
|
||||
MEMORY_REPORTING_LOG("TimeoutCallback (s->gen=%u)\n",
|
||||
mgr->mGetReportsState->mGeneration);
|
||||
|
||||
// We don't bother sending any kind of cancellation message to the child
|
||||
// processes that haven't reported back.
|
||||
|
||||
mgr->FinishReporting();
|
||||
}
|
||||
|
||||
void
|
||||
nsMemoryReporterManager::FinishReporting()
|
||||
{
|
||||
// Memory reporting only happens on the main thread.
|
||||
if (!NS_IsMainThread()) {
|
||||
MOZ_CRASH();
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mGetReportsState);
|
||||
MEMORY_REPORTING_LOG("FinishReporting (s->gen=%u)\n",
|
||||
mGetReportsState->mGeneration);
|
||||
|
||||
// Call this before deleting |mGetReportsState|. That way, if
|
||||
// |mFinishReportData| calls GetReports(), it will silently abort, as
|
||||
// required.
|
||||
(void)mGetReportsState->mFinishReporting->Callback(
|
||||
mGetReportsState->mFinishReportingData);
|
||||
|
||||
delete mGetReportsState;
|
||||
mGetReportsState = nullptr;
|
||||
}
|
||||
|
||||
static void
|
||||
DebugAssertRefcountIsNonZero(nsISupports* aObj)
|
||||
{
|
||||
@ -975,7 +1192,7 @@ public:
|
||||
};
|
||||
NS_IMPL_ISUPPORTS0(Int64Wrapper)
|
||||
|
||||
class ExplicitCallback MOZ_FINAL : public nsIMemoryReporterCallback
|
||||
class ExplicitCallback MOZ_FINAL : public nsIHandleReportCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -1002,7 +1219,7 @@ public:
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
NS_IMPL_ISUPPORTS1(ExplicitCallback, nsIMemoryReporterCallback)
|
||||
NS_IMPL_ISUPPORTS1(ExplicitCallback, nsIHandleReportCallback)
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsMemoryReporterManager::GetExplicit(int64_t* aAmount)
|
||||
@ -1020,7 +1237,7 @@ nsMemoryReporterManager::GetExplicit(int64_t* aAmount)
|
||||
// method which did this more efficiently, but it ended up being more
|
||||
// trouble than it was worth.
|
||||
|
||||
nsRefPtr<ExplicitCallback> cb = new ExplicitCallback();
|
||||
nsRefPtr<ExplicitCallback> handleReport = new ExplicitCallback();
|
||||
nsRefPtr<Int64Wrapper> wrappedExplicitSize = new Int64Wrapper();
|
||||
|
||||
nsCOMPtr<nsISimpleEnumerator> e;
|
||||
@ -1028,7 +1245,7 @@ nsMemoryReporterManager::GetExplicit(int64_t* aAmount)
|
||||
while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
|
||||
nsCOMPtr<nsIMemoryReporter> r;
|
||||
e->GetNext(getter_AddRefs(r));
|
||||
r->CollectReports(cb, wrappedExplicitSize);
|
||||
r->CollectReports(handleReport, wrappedExplicitSize);
|
||||
}
|
||||
|
||||
*aAmount = wrappedExplicitSize->mValue;
|
||||
@ -1349,10 +1566,8 @@ NS_UnregisterMemoryReporter(nsIMemoryReporter* aReporter)
|
||||
namespace mozilla {
|
||||
|
||||
#define GET_MEMORY_REPORTER_MANAGER(mgr) \
|
||||
nsCOMPtr<nsIMemoryReporterManager> imgr = \
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1"); \
|
||||
nsRefPtr<nsMemoryReporterManager> mgr = \
|
||||
static_cast<nsMemoryReporterManager*>(imgr.get()); \
|
||||
nsMemoryReporterManager::GetOrCreate(); \
|
||||
if (!mgr) { \
|
||||
return NS_ERROR_FAILURE; \
|
||||
}
|
||||
@ -1418,7 +1633,7 @@ DEFINE_REGISTER_SIZE_OF_TAB(NonJS);
|
||||
namespace mozilla {
|
||||
namespace dmd {
|
||||
|
||||
class NullReporterCallback : public nsIMemoryReporterCallback
|
||||
class DoNothingCallback : public nsIHandleReportCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
@ -1433,8 +1648,8 @@ public:
|
||||
}
|
||||
};
|
||||
NS_IMPL_ISUPPORTS1(
|
||||
NullReporterCallback
|
||||
, nsIMemoryReporterCallback
|
||||
DoNothingCallback
|
||||
, nsIHandleReportCallback
|
||||
)
|
||||
|
||||
void
|
||||
@ -1443,7 +1658,7 @@ RunReporters()
|
||||
nsCOMPtr<nsIMemoryReporterManager> mgr =
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
|
||||
nsRefPtr<NullReporterCallback> cb = new NullReporterCallback();
|
||||
nsRefPtr<DoNothingCallback> doNothing = new DoNothingCallback();
|
||||
|
||||
bool more;
|
||||
nsCOMPtr<nsISimpleEnumerator> e;
|
||||
@ -1451,7 +1666,7 @@ RunReporters()
|
||||
while (NS_SUCCEEDED(e->HasMoreElements(&more)) && more) {
|
||||
nsCOMPtr<nsIMemoryReporter> r;
|
||||
e->GetNext(getter_AddRefs(r));
|
||||
r->CollectReports(cb, nullptr);
|
||||
r->CollectReports(doNothing, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -11,6 +11,14 @@
|
||||
|
||||
using mozilla::Mutex;
|
||||
|
||||
class nsITimer;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class MemoryReport;
|
||||
}
|
||||
}
|
||||
|
||||
class nsMemoryReporterManager : public nsIMemoryReporterManager
|
||||
{
|
||||
public:
|
||||
@ -20,6 +28,93 @@ public:
|
||||
nsMemoryReporterManager();
|
||||
virtual ~nsMemoryReporterManager();
|
||||
|
||||
// Gets the memory reporter manager service.
|
||||
static nsMemoryReporterManager* GetOrCreate()
|
||||
{
|
||||
nsCOMPtr<nsIMemoryReporterManager> imgr =
|
||||
do_GetService("@mozilla.org/memory-reporter-manager;1");
|
||||
return static_cast<nsMemoryReporterManager*>(imgr.get());
|
||||
}
|
||||
|
||||
void IncrementNumChildProcesses();
|
||||
void DecrementNumChildProcesses();
|
||||
|
||||
// Inter-process memory reporting proceeds as follows.
|
||||
//
|
||||
// - GetReports() (declared within NS_DECL_NSIMEMORYREPORTERMANAGER)
|
||||
// synchronously gets memory reports for the current process, tells all
|
||||
// child processes to get memory reports, and sets up some state
|
||||
// (mGetReportsState) for when the child processes report back, including a
|
||||
// timer. Control then returns to the main event loop.
|
||||
//
|
||||
// - HandleChildReports() is called (asynchronously) once per child process
|
||||
// that reports back. If all child processes report back before time-out,
|
||||
// the timer is cancelled. (The number of child processes is part of the
|
||||
// saved request state.)
|
||||
//
|
||||
// - TimeoutCallback() is called (asynchronously) if all the child processes
|
||||
// don't respond within the time threshold.
|
||||
//
|
||||
// - FinishReporting() finishes things off. It is *always* called -- either
|
||||
// from HandleChildReports() (if all child processes have reported back) or
|
||||
// from TimeoutCallback() (if time-out occurs).
|
||||
//
|
||||
// All operations occur on the main thread.
|
||||
//
|
||||
// The above sequence of steps is a "request". A partially-completed request
|
||||
// is described as "in flight".
|
||||
//
|
||||
// Each request has a "generation", a unique number that identifies it. This
|
||||
// is used to ensure that each reports from a child process corresponds to
|
||||
// the appropriate request from the parent process. (It's easier to
|
||||
// implement a generation system than to implement a child report request
|
||||
// cancellation mechanism.)
|
||||
//
|
||||
// Failures are mostly ignored, because it's (a) typically the most sensible
|
||||
// thing to do, and (b) often hard to do anything else. The following are
|
||||
// the failure cases of note.
|
||||
//
|
||||
// - If a request is made while the previous request is in flight, the new
|
||||
// request is ignored, as per getReports()'s specification. No error is
|
||||
// reported, because the previous request will complete soon enough.
|
||||
//
|
||||
// - If one or more child processes fail to respond within the time limit,
|
||||
// things will proceed as if they don't exist. No error is reported,
|
||||
// because partial information is better than nothing.
|
||||
//
|
||||
// - If a child process reports after the time-out occurs, it is ignored.
|
||||
// (Generation checking will ensure it is ignored even if a subsequent
|
||||
// request is in flight; this is the main use of generations.) No error
|
||||
// is reported, because there's nothing sensible to be done about it at
|
||||
// this late stage.
|
||||
//
|
||||
// Now, what what happens if a child process is created/destroyed in the
|
||||
// middle of a request? Well, GetReportsState contains a copy of
|
||||
// mNumChildProcesses which it uses to determine finished-ness. So...
|
||||
//
|
||||
// - If a process is created, it won't have received the request for reports,
|
||||
// and the GetReportsState's mNumChildProcesses won't account for it. So
|
||||
// the reported data will reflect how things were when the request began.
|
||||
//
|
||||
// - If a process is destroyed before reporting back, we'll just hit the
|
||||
// time-out, because we'll have received reports (barring other errors)
|
||||
// from N-1 child process. So the reported data will reflect how things
|
||||
// are when the request ends.
|
||||
//
|
||||
// - If a process is destroyed after reporting back, but before all other
|
||||
// child processes have reported back, it will be included in the reported
|
||||
// data. So the reported data will reflect how things were when the
|
||||
// request began.
|
||||
//
|
||||
// The inconsistencies between these three cases are unfortunate but
|
||||
// difficult to avoid. It's enough of an edge case to not be worth doing
|
||||
// more.
|
||||
//
|
||||
void HandleChildReports(
|
||||
const uint32_t& generation,
|
||||
const InfallibleTArray<mozilla::dom::MemoryReport>& aChildReports);
|
||||
void FinishReporting();
|
||||
|
||||
// Functions that (a) implement distinguished amounts, and (b) are outside of
|
||||
// this module.
|
||||
struct AmountFns {
|
||||
@ -36,6 +131,8 @@ public:
|
||||
mozilla::InfallibleAmountFn mLowMemoryEventsPhysical;
|
||||
|
||||
mozilla::InfallibleAmountFn mGhostWindows;
|
||||
|
||||
AmountFns() { mozilla::PodZero(this); }
|
||||
};
|
||||
AmountFns mAmountFns;
|
||||
|
||||
@ -43,15 +140,55 @@ public:
|
||||
struct SizeOfTabFns {
|
||||
mozilla::JSSizeOfTabFn mJS;
|
||||
mozilla::NonJSSizeOfTabFn mNonJS;
|
||||
|
||||
SizeOfTabFns() { mozilla::PodZero(this); }
|
||||
};
|
||||
SizeOfTabFns mSizeOfTabFns;
|
||||
|
||||
private:
|
||||
nsresult RegisterReporterHelper(nsIMemoryReporter *aReporter, bool aForce);
|
||||
nsresult RegisterReporterHelper(nsIMemoryReporter* aReporter, bool aForce);
|
||||
|
||||
static void TimeoutCallback(nsITimer* aTimer, void* aData);
|
||||
static const uint32_t kTimeoutLengthMS = 5000;
|
||||
|
||||
nsTHashtable<nsISupportsHashKey> mReporters;
|
||||
Mutex mMutex;
|
||||
bool mIsRegistrationBlocked;
|
||||
|
||||
uint32_t mNumChildProcesses;
|
||||
uint32_t mNextGeneration;
|
||||
|
||||
struct GetReportsState {
|
||||
uint32_t mGeneration;
|
||||
nsCOMPtr<nsITimer> mTimer;
|
||||
uint32_t mNumChildProcesses;
|
||||
uint32_t mNumChildProcessesCompleted;
|
||||
nsCOMPtr<nsIHandleReportCallback> mHandleReport;
|
||||
nsCOMPtr<nsISupports> mHandleReportData;
|
||||
nsCOMPtr<nsIFinishReportingCallback> mFinishReporting;
|
||||
nsCOMPtr<nsISupports> mFinishReportingData;
|
||||
|
||||
GetReportsState(uint32_t aGeneration, nsITimer* aTimer,
|
||||
uint32_t aNumChildProcesses,
|
||||
nsIHandleReportCallback* aHandleReport,
|
||||
nsISupports* aHandleReportData,
|
||||
nsIFinishReportingCallback* aFinishReporting,
|
||||
nsISupports* aFinishReportingData)
|
||||
: mGeneration(aGeneration),
|
||||
mTimer(aTimer),
|
||||
mNumChildProcesses(aNumChildProcesses),
|
||||
mNumChildProcessesCompleted(0),
|
||||
mHandleReport(aHandleReport),
|
||||
mHandleReportData(aHandleReportData),
|
||||
mFinishReporting(aFinishReporting),
|
||||
mFinishReportingData(aFinishReportingData)
|
||||
{}
|
||||
};
|
||||
|
||||
// When this is non-null, a request is in flight. Note: We use manual
|
||||
// new/delete for this because its lifetime doesn't match block scope or
|
||||
// anything like that.
|
||||
GetReportsState* mGetReportsState;
|
||||
};
|
||||
|
||||
#define NS_MEMORY_REPORTER_MANAGER_CID \
|
||||
|
Loading…
Reference in New Issue
Block a user