Make the memory reporting multi-process aware (#35863)

So far the memory reporter aggregates reports from all processes, and
runs the system reporter only in the main process. Instead it is
desirable to have per-process reports. We do so by:
- creating a ProcessReports struct that holds includes the pid in
addition to the reports themselves.
- running the system memory reporter also in content processes.
- updating the about:memory page to create one report per process, and
add useful information like the pid and the urls loaded in a given
process.

<!-- Please describe your changes on the following line: -->


---
<!-- Thank you for contributing to Servo! Please replace each `[ ]` by
`[X]` when the step is complete, and replace `___` with appropriate
data: -->
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors


![image](https://github.com/user-attachments/assets/0bafe140-539d-4d6a-8316-639309a22d4a)

Signed-off-by: webbeef <me@webbeef.org>
This commit is contained in:
webbeef 2025-04-04 22:42:12 -07:00 committed by GitHub
parent 76edcff202
commit aef8537d75
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 551 additions and 424 deletions

View file

@ -63,6 +63,96 @@
return result;
}
function reportsForProcess(processReport) {
let explicitRoot = {};
let nonExplicitRoot = {};
let jemallocHeapReportedSize = 0;
let systemHeapReportedSize = 0;
let jemallocHeapAllocatedSize = NaN;
let systemHeapAllocatedSize = NaN;
// In content processes, get the list of urls.
let urls = new Set();
processReport.reports.forEach((report) => {
if (report.path[0].startsWith("url(")) {
// This can be a list of urls.
let path_urls = report.path[0].slice(4, -1).split(", ");
path_urls.forEach((url) => urls.add(url));
}
// Add "explicit" to the start of the path, when appropriate.
if (report.kind.startsWith("Explicit")) {
report.path.unshift("explicit");
}
// Update the reported fractions of the heaps, when appropriate.
if (report.kind == "ExplicitJemallocHeapSize") {
jemallocHeapReportedSize += report.size;
} else if (report.kind == "ExplicitSystemHeapSize") {
systemHeapReportedSize += report.size;
}
// Record total size of the heaps, when we see them.
if (report.path.length == 1) {
if (report.path[0] == "jemalloc-heap-allocated") {
jemallocHeapAllocatedSize = report.size;
} else if (report.path[0] == "system-heap-allocated") {
systemHeapAllocatedSize = report.size;
}
}
// Insert this report at the proper position.
insertNode(
report.kind.startsWith("Explicit") ? explicitRoot : nonExplicitRoot,
report
);
});
// Compute and insert the heap-unclassified values.
if (!isNaN(jemallocHeapAllocatedSize)) {
insertNode(explicitRoot, {
path: ["explicit", "jemalloc-heap-unclassified"],
size: jemallocHeapAllocatedSize - jemallocHeapReportedSize,
});
}
if (!isNaN(systemHeapAllocatedSize)) {
insertNode(explicitRoot, {
path: ["explicit", "system-heap-unclassified"],
size: systemHeapAllocatedSize - systemHeapReportedSize,
});
}
// Create the DOM structure for each process report:
// <div class=process> <h4>...<h4> <pre> ...</pre> </div>
let container = document.createElement("div");
container.classList.add("process");
let reportTitle = document.createElement("h4");
reportTitle.textContent = `${
processReport.isMainProcess ? "Main Process" : "Content Process"
} (pid ${processReport.pid}) ${[...urls.values()].join(", ")}`;
container.append(reportTitle);
let reportNode = document.createElement("pre");
reportNode.classList.add("report");
container.append(reportNode);
reportNode.append(convertNodeToDOM(explicitRoot.explicit, "explicit"));
for (let prop in nonExplicitRoot) {
reportNode.append(convertNodeToDOM(nonExplicitRoot[prop], prop));
}
// Make sure we always put the main process first.
if (processReport.isMainProcess) {
window.reports.prepend(container);
} else {
window.reports.append(container);
}
}
function start() {
window.startButton.onclick = async () => {
let content = await navigator.servo.reportMemory();
@ -71,70 +161,15 @@
console.error(reports.error);
return;
}
window.report.innerHTML = "";
window.report.classList.remove("hidden");
window.reports.innerHTML = "";
window.reports.classList.remove("hidden");
let explicitRoot = {};
let nonExplicitRoot = {};
let jemallocHeapReportedSize = 0;
let systemHeapReportedSize = 0;
let jemallocHeapAllocatedSize = NaN;
let systemHeapAllocatedSize = NaN;
reports.forEach((report) => {
// Add "explicit" to the start of the path, when appropriate.
if (report.kind.startsWith("Explicit")) {
report.path.unshift("explicit");
}
// Update the reported fractions of the heaps, when appropriate.
if (report.kind == "ExplicitJemallocHeapSize") {
jemallocHeapReportedSize += report.size;
} else if (report.kind == "ExplicitSystemHeapSize") {
systemHeapReportedSize += report.size;
}
// Record total size of the heaps, when we see them.
if (report.path.length == 1) {
if (report.path[0] == "jemalloc-heap-allocated") {
jemallocHeapAllocatedSize = report.size;
} else if (report.path[0] == "system-heap-allocated") {
systemHeapAllocatedSize = report.size;
}
}
// Insert this report at the proper position.
insertNode(
report.kind.startsWith("Explicit")
? explicitRoot
: nonExplicitRoot,
report
);
});
// Compute and insert the heap-unclassified values.
if (!isNaN(jemallocHeapAllocatedSize)) {
insertNode(explicitRoot, {
path: ["explicit", "jemalloc-heap-unclassified"],
size: jemallocHeapAllocatedSize - jemallocHeapReportedSize,
});
}
if (!isNaN(systemHeapAllocatedSize)) {
insertNode(explicitRoot, {
path: ["explicit", "system-heap-unclassified"],
size: systemHeapAllocatedSize - systemHeapReportedSize,
});
if (!Array.isArray(reports)) {
console.error("Unexpected memory report format!");
return;
}
window.report.append(
convertNodeToDOM(explicitRoot.explicit, "explicit")
);
for (let prop in nonExplicitRoot) {
window.report.append(convertNodeToDOM(nonExplicitRoot[prop], prop));
}
reports.forEach(reportsForProcess);
};
}
</script>
@ -152,15 +187,19 @@
cursor: pointer;
}
#report {
line-height: 1.5em;
div.process {
margin: 0.5em;
border: 2px solid gray;
border-radius: 10px;
padding: 5px;
background-color: lightgray;
}
#report > details {
.report {
line-height: 1.5em;
}
.report > details {
margin-bottom: 1em;
}
@ -172,6 +211,6 @@
<body>
<h2>Memory Reports</h2>
<button id="startButton">Measure</button>
<pre id="report" class="hidden"></pre>
<div id="reports" class="hidden"></div>
</body>
</html>