mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-21 09:15:35 +00:00
7b0a1f6dee
There are two leaks addressed in this commit: 1. The thread actor's `_debuggerSourcesSeen` set was never cleared. This set exists only as a performance optimization to speed up `_addSource` in cases where we've already added the source. Unfortunately, this set wasn't getting cleared when we cleared debuggees out and it ended up keeping the `Debugger.Source`, its referent, and transitively its referent's global alive. I figured it was simpler to make it a `WeakSet` than to add it as a special case in `ThreadActor.prototype._clearDebuggees` and manage the lifetimes by hand. I think this fits well with its intended use as an ephemeral performance optimization. 2. Due to a logic error, we were not clearing debuggees in the memory actor's `Debugger` instance on navigations. This isn't really a "proper" leak, in that if you forced a GC, the old debuggees would go away as `Debugger` holds them weakly, however if there was no GC between navigations, then you could still see the old windows (and everything they "retained") as roots in the snapshot. This issue is straightforward to fix once identified: ensure that `_clearDebuggees` is actually called on navigation. Finally, this commit adds a test that we don't leak Window objects when devtools are open and we keep refreshing a tab. When it fails, it prints out the leaking window's retaining paths.
125 lines
3.0 KiB
JavaScript
125 lines
3.0 KiB
JavaScript
/* 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/. */
|
|
"use strict";
|
|
|
|
const {
|
|
Arg,
|
|
RetVal,
|
|
types,
|
|
generateActorSpec,
|
|
} = require("devtools/shared/protocol");
|
|
|
|
types.addDictType("AllocationsRecordingOptions", {
|
|
// The probability we sample any given allocation when recording
|
|
// allocations. Must be between 0.0 and 1.0. Defaults to 1.0, or sampling
|
|
// every allocation.
|
|
probability: "number",
|
|
|
|
// The maximum number of of allocation events to keep in the allocations
|
|
// log. If new allocations arrive, when we are already at capacity, the oldest
|
|
// allocation event is lost. This number must fit in a 32 bit signed integer.
|
|
maxLogLength: "number"
|
|
});
|
|
|
|
const memorySpec = generateActorSpec({
|
|
typeName: "memory",
|
|
|
|
/**
|
|
* The set of unsolicited events the MemoryActor emits that will be sent over
|
|
* the RDP (by protocol.js).
|
|
*/
|
|
events: {
|
|
// Same format as the data passed to the
|
|
// `Debugger.Memory.prototype.onGarbageCollection` hook. See
|
|
// `js/src/doc/Debugger/Debugger.Memory.md` for documentation.
|
|
"garbage-collection": {
|
|
type: "garbage-collection",
|
|
data: Arg(0, "json"),
|
|
},
|
|
|
|
// Same data as the data from `getAllocations` -- only fired if
|
|
// `autoDrain` set during `startRecordingAllocations`.
|
|
"allocations": {
|
|
type: "allocations",
|
|
data: Arg(0, "json"),
|
|
},
|
|
},
|
|
|
|
methods: {
|
|
attach: {
|
|
request: {},
|
|
response: {
|
|
type: "attached"
|
|
}
|
|
},
|
|
detach: {
|
|
request: {},
|
|
response: {
|
|
type: "detached"
|
|
}
|
|
},
|
|
getState: {
|
|
response: {
|
|
state: RetVal(0, "string")
|
|
}
|
|
},
|
|
takeCensus: {
|
|
request: {},
|
|
response: RetVal("json")
|
|
},
|
|
startRecordingAllocations: {
|
|
request: {
|
|
options: Arg(0, "nullable:AllocationsRecordingOptions")
|
|
},
|
|
response: {
|
|
// Accept `nullable` in the case of server Gecko <= 37, handled on the front
|
|
value: RetVal(0, "nullable:number")
|
|
}
|
|
},
|
|
stopRecordingAllocations: {
|
|
request: {},
|
|
response: {
|
|
// Accept `nullable` in the case of server Gecko <= 37, handled on the front
|
|
value: RetVal(0, "nullable:number")
|
|
}
|
|
},
|
|
getAllocationsSettings: {
|
|
request: {},
|
|
response: {
|
|
options: RetVal(0, "json")
|
|
}
|
|
},
|
|
getAllocations: {
|
|
request: {},
|
|
response: RetVal("json")
|
|
},
|
|
forceGarbageCollection: {
|
|
request: {},
|
|
response: {}
|
|
},
|
|
forceCycleCollection: {
|
|
request: {},
|
|
response: {}
|
|
},
|
|
measure: {
|
|
request: {},
|
|
response: RetVal("json"),
|
|
},
|
|
residentUnique: {
|
|
request: {},
|
|
response: { value: RetVal("number") }
|
|
},
|
|
saveHeapSnapshot: {
|
|
request: {
|
|
boundaries: Arg(0, "nullable:json")
|
|
},
|
|
response: {
|
|
snapshotId: RetVal("string")
|
|
}
|
|
},
|
|
},
|
|
});
|
|
|
|
exports.memorySpec = memorySpec;
|