Memory leak when awaiting in inspector `Debugger.pause` callback
Version
20.10.0
Platform
All
Subsystem
inspector
What steps will reproduce the bug?
The following code leaks memory at about 10MB per second on my machine:
import { Session } from "node:inspector/promises";
const session = new Session();
session.connect();
session.on("Debugger.paused", async (event) => {
for (const frame of event.params.callFrames) {
const localScope = frame.scopeChain.find((scope) => scope.type === "local");
if (localScope?.object?.objectId) {
const props = await session.post("Runtime.getProperties", {
objectId: localScope.object.objectId,
ownProperties: true,
});
}
}
});
await session.post("Debugger.enable");
await session.post("Debugger.setPauseOnExceptions", { state: "all" });
setInterval(() => {
console.log(`${Math.floor(process.memoryUsage().rss / 1024 / 1024)} MB`);
}, 1000);
setInterval(() => {
try {
throw new Error("Hello");
} catch {}
}, 1);
How often does it reproduce? Is there a required condition?
Every time
What is the expected behavior? Why is that the expected behavior?
No memory leaked
What do you see instead?
I see ~10MB leaked every second which is around 10kB per debugger pause event.
Additional information
I found that that the following code:
import { Session } from "node:inspector/promises";
const session = new Session();
session.connect();
session.on("Debugger.resumed", () => {
console.log("Debugger resumed");
});
session.on("Debugger.paused", async (event) => {
console.log("Debugger paused start");
for (const frame of event.params.callFrames) {
const localScope = frame.scopeChain.find((scope) => scope.type === "local");
if (localScope?.object?.objectId) {
console.log("Runtime.getProperties");
const props = await session.post("Runtime.getProperties", {
objectId: localScope.object.objectId,
});
console.log("Runtime.getProperties returned");
}
}
console.log("Debugger.paused end");
});
await session.post("Debugger.enable");
await session.post("Debugger.setPauseOnExceptions", { state: "all" });
setTimeout(() => {
try {
throw new Error("Hello");
} catch {}
}, 1000);
Outputs:
Debugger paused start
Runtime.getProperties
Debugger resumed
Runtime.getProperties returned
Runtime.getProperties
Runtime.getProperties returned
Runtime.getProperties
Runtime.getProperties returned
Debugger.paused end
The debugger does not wait for Debugger.resume
. Instead it resumes automatically as soon as we call await session.post
and I guess calling Runtime.getProperties
after the resume causes something to be leaked?