/* 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/. */
/** * A class that returns memory data for a parent actor's window. * Using a target-scoped actor with this instance will measure the memory footprint of its * parent tab. Using a global-scoped actor instance however, will measure the memory * footprint of the chrome window referenced by its root actor. * * To be consumed by actor's, like MemoryActor using this module to * send information over RDP, and TimelineActor for using more light-weight * utilities like GC events and measuring memory consumption.
*/ function Memory(parent, frameCache = new StackFrameCache()) {
EventEmitter.decorate(this);
get dbg() { if (!this._dbg) { this._dbg = this.parent.makeDebugger();
} returnthis._dbg;
},
/** * Attach to this MemoryBridge. * * This attaches the MemoryBridge's Debugger instance so that you can start * recording allocations or take a census of the heap. In addition, the * MemoryBridge will start emitting GC events.
*/
attach() { // The actor may be attached by the Target via recordAllocation configuration // or manually by the frontend. if (this.state == "attached") { returnthis.state;
} this.dbg.addDebuggees(); this.dbg.memory.onGarbageCollection = this._onGarbageCollection.bind(this); this.state = "attached"; returnthis.state;
},
/** * Detach from this MemoryBridge.
*/
detach: expectState( "attached", function () { this._clearDebuggees(); this.dbg.disable(); this._dbg = null; this.state = "detached"; returnthis.state;
}, "detaching from the debugger"
),
/** * Gets the current MemoryBridge attach/detach state.
*/
getState() { returnthis.state;
},
_clearDebuggees() { if (this._dbg) { if (this.isRecordingAllocations()) { this.dbg.memory.drainAllocationsLog();
} this._clearFrames(); this.dbg.removeAllDebuggees();
}
},
_clearFrames() { if (this.isRecordingAllocations()) { this._frameCache.clearFrames();
}
},
/** * Handler for the parent actor's "window-ready" event.
*/
_onWindowReady({ isTopLevel }) { if (this.state == "attached") { this._clearDebuggees(); if (isTopLevel && this.isRecordingAllocations()) { this._frameCache.initFrames();
} this.dbg.addDebuggees();
}
},
/** * Returns a boolean indicating whether or not allocation * sites are being tracked.
*/
isRecordingAllocations() { returnthis.dbg.memory.trackingAllocationSites;
},
/** * Save a heap snapshot scoped to the current debuggees' portion of the heap * graph. * * @param {Object|null} boundaries * * @returns {String} The snapshot id.
*/
saveHeapSnapshot: expectState( "attached", function (boundaries = null) { // If we are observing the whole process, then scope the snapshot // accordingly. Otherwise, use the debugger's debuggees. if (!boundaries) { if ( this.parent instanceof ParentProcessTargetActor || this.parent instanceof ContentProcessTargetActor
) {
boundaries = { runtime: true };
} else {
boundaries = { debugger: this.dbg };
}
} return ChromeUtils.saveHeapSnapshotGetId(boundaries);
}, "saveHeapSnapshot"
),
/** * Take a census of the heap. See js/src/doc/Debugger/Debugger.Memory.md for * more information.
*/
takeCensus: expectState( "attached", function () { returnthis.dbg.memory.takeCensus();
}, "taking census"
),
/** * Start recording allocation sites. * * @param {number} options.probability * The probability we sample any given allocation when recording * allocations. Must be between 0 and 1 -- defaults to 1. * @param {number} options.maxLogLength * The maximum number of allocation events to keep in the * log. If new allocs occur while at capacity, oldest * allocations are lost. Must fit in a 32 bit signed integer. * @param {number} options.drainAllocationsTimeout * A number in milliseconds of how often, at least, an `allocation` * event gets emitted (and drained), and also emits and drains on every * GC event, resetting the timer.
*/
startRecordingAllocations: expectState( "attached", function (options = {}) { if (this.isRecordingAllocations()) { returnthis._getCurrentTime();
}
/** * Return settings used in `startRecordingAllocations` for `probability` * and `maxLogLength`. Currently only uses in tests.
*/
getAllocationsSettings: expectState( "attached", function () { return {
maxLogLength: this.dbg.memory.maxAllocationsLogLength,
probability: this.dbg.memory.allocationSamplingProbability,
};
}, "getting allocations settings"
),
/** * Get a list of the most recent allocations since the last time we got * allocations, as well as a summary of all allocations since we've been * recording. * * @returns Object * An object of the form: * * { * allocations: [<index into "frames" below>, ...], * allocationsTimestamps: [ * <timestamp for allocations[0]>, * <timestamp for allocations[1]>, * ... * ], * allocationSizes: [ * <bytesize for allocations[0]>, * <bytesize for allocations[1]>, * ... * ], * frames: [ * { * line: <line number for this frame>, * column: <column number for this frame>, * source: <filename string for this frame>, * functionDisplayName: * <this frame's inferred function name function or null>, * parent: <index into "frames"> * }, * ... * ], * } * * The timestamps' unit is microseconds since the epoch. * * Subsequent `getAllocations` request within the same recording and * tab navigation will always place the same stack frames at the same * indices as previous `getAllocations` requests in the same * recording. In other words, it is safe to use the index as a * unique, persistent id for its frame. * * Additionally, the root node (null) is always at index 0. * * We use the indices into the "frames" array to avoid repeating the * description of duplicate stack frames both when listing * allocations, and when many stacks share the same tail of older * frames. There shouldn't be any duplicates in the "frames" array, * as that would defeat the purpose of this compression trick. * * In the future, we might want to split out a frame's "source" and * "functionDisplayName" properties out the same way we have split * frames out with the "frames" array. While this would further * compress the size of the response packet, it would increase CPU * usage to build the packet, and it should, of course, be guided by * profiling and done only when necessary.
*/
getAllocations: expectState( "attached", function () { if (this.dbg.memory.allocationsLogOverflowed) { // Since the last time we drained the allocations log, there have been // more allocations than the log's capacity, and we lost some data. There // isn't anything actionable we can do about this, but put a message in // the browser console so we at least know that it occurred.
reportException( "MemoryBridge.prototype.getAllocations", "Warning: allocations log overflowed and lost some data."
);
}
// Safe because SavedFrames are frozen/immutable. const waived = Cu.waiveXrays(stack);
// Ensure that we have a form, size, and index for new allocations // because we potentially haven't seen some or all of them yet. After this // loop, we can rely on the fact that every frame we deal with already has // its metadata stored. const index = this._frameCache.addFrame(waived);
/** * A method that returns a detailed breakdown of the memory consumption of the * associated window. * * @returns object
*/
measure() { const result = {};
/** * Handler for GC events on the Debugger.Memory instance.
*/
_onGarbageCollection(data) { this.emit("garbage-collection", data);
// If `drainAllocationsTimeout` set, fire an allocations event with the drained log, // which will restart the timer. if (this._poller) { this._poller.disarm(); this._emitAllocations();
}
},
/** * Called on `drainAllocationsTimeoutTimer` interval if and only if set * during `startRecordingAllocations`, or on a garbage collection event if * drainAllocationsTimeout was set. * Drains allocation log and emits as an event and restarts the timer.
*/
_emitAllocations() { this.emit("allocations", this.getAllocations()); this._poller.arm();
},
/** * Accesses the docshell to return the current process time.
*/
_getCurrentTime() { const docShell = this.parent.isRootActor
? this.parent.docShell
: this.parent.originalDocShell; if (docShell) { return docShell.now();
} // When used from the ContentProcessTargetActor, parent has no docShell, // so fallback to Cu.now return Cu.now();
},
};
exports.Memory = Memory;
¤ Dauer der Verarbeitung: 0.27 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.