mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-05 08:35:26 +00:00
959c1c861a
--HG-- rename : devtools/shared/shared/framerate.js => devtools/server/performance/framerate.js rename : devtools/shared/shared/memory.js => devtools/server/performance/memory.js rename : devtools/shared/shared/profiler.js => devtools/server/performance/profiler.js
256 lines
7.8 KiB
JavaScript
256 lines
7.8 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 { Cc, Ci, Cu, components } = require("chrome");
|
|
const protocol = require("devtools/server/protocol");
|
|
const { method, RetVal, Arg, types } = protocol;
|
|
const { Memory } = require("devtools/server/performance/memory");
|
|
const { actorBridge } = require("devtools/server/actors/common");
|
|
loader.lazyRequireGetter(this, "events", "sdk/event/core");
|
|
loader.lazyRequireGetter(this, "StackFrameCache",
|
|
"devtools/server/actors/utils/stack", true);
|
|
loader.lazyRequireGetter(this, "FileUtils",
|
|
"resource://gre/modules/FileUtils.jsm", true);
|
|
loader.lazyRequireGetter(this, "NetUtil", "resource://gre/modules/NetUtil.jsm", true);
|
|
loader.lazyRequireGetter(this, "Task", "resource://gre/modules/Task.jsm", true);
|
|
loader.lazyRequireGetter(this, "HeapSnapshotFileUtils",
|
|
"devtools/shared/heapsnapshot/HeapSnapshotFileUtils");
|
|
loader.lazyRequireGetter(this, "ThreadSafeChromeUtils");
|
|
|
|
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"
|
|
});
|
|
|
|
/**
|
|
* An actor that returns memory usage data for its parent actor's window.
|
|
* A tab-scoped instance of this actor will measure the memory footprint of its
|
|
* parent tab. A global-scoped instance however, will measure the memory
|
|
* footprint of the chrome window referenced by the root actor.
|
|
*
|
|
* This actor wraps the Memory module at devtools/server/performance/memory.js
|
|
* and provides RDP definitions.
|
|
*
|
|
* @see devtools/server/performance/memory.js for documentation.
|
|
*/
|
|
var MemoryActor = exports.MemoryActor = protocol.ActorClass({
|
|
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"),
|
|
},
|
|
},
|
|
|
|
initialize: function(conn, parent, frameCache = new StackFrameCache()) {
|
|
protocol.Actor.prototype.initialize.call(this, conn);
|
|
|
|
this._onGarbageCollection = this._onGarbageCollection.bind(this);
|
|
this._onAllocations = this._onAllocations.bind(this);
|
|
this.bridge = new Memory(parent, frameCache);
|
|
this.bridge.on("garbage-collection", this._onGarbageCollection);
|
|
this.bridge.on("allocations", this._onAllocations);
|
|
},
|
|
|
|
destroy: function() {
|
|
this.bridge.off("garbage-collection", this._onGarbageCollection);
|
|
this.bridge.off("allocations", this._onAllocations);
|
|
this.bridge.destroy();
|
|
protocol.Actor.prototype.destroy.call(this);
|
|
},
|
|
|
|
attach: actorBridge("attach", {
|
|
request: {},
|
|
response: {
|
|
type: "attached"
|
|
}
|
|
}),
|
|
|
|
detach: actorBridge("detach", {
|
|
request: {},
|
|
response: {
|
|
type: "detached"
|
|
}
|
|
}),
|
|
|
|
getState: actorBridge("getState", {
|
|
response: {
|
|
state: RetVal(0, "string")
|
|
}
|
|
}),
|
|
|
|
saveHeapSnapshot: method(function () {
|
|
return this.bridge.saveHeapSnapshot();
|
|
}, {
|
|
response: {
|
|
snapshotId: RetVal("string")
|
|
}
|
|
}),
|
|
|
|
takeCensus: actorBridge("takeCensus", {
|
|
request: {},
|
|
response: RetVal("json")
|
|
}),
|
|
|
|
startRecordingAllocations: actorBridge("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: actorBridge("stopRecordingAllocations", {
|
|
request: {},
|
|
response: {
|
|
// Accept `nullable` in the case of server Gecko <= 37, handled on the front
|
|
value: RetVal(0, "nullable:number")
|
|
}
|
|
}),
|
|
|
|
getAllocationsSettings: actorBridge("getAllocationsSettings", {
|
|
request: {},
|
|
response: {
|
|
options: RetVal(0, "json")
|
|
}
|
|
}),
|
|
|
|
getAllocations: actorBridge("getAllocations", {
|
|
request: {},
|
|
response: RetVal("json")
|
|
}),
|
|
|
|
forceGarbageCollection: actorBridge("forceGarbageCollection", {
|
|
request: {},
|
|
response: {}
|
|
}),
|
|
|
|
forceCycleCollection: actorBridge("forceCycleCollection", {
|
|
request: {},
|
|
response: {}
|
|
}),
|
|
|
|
measure: actorBridge("measure", {
|
|
request: {},
|
|
response: RetVal("json"),
|
|
}),
|
|
|
|
residentUnique: actorBridge("residentUnique", {
|
|
request: {},
|
|
response: { value: RetVal("number") }
|
|
}),
|
|
|
|
_onGarbageCollection: function (data) {
|
|
if (this.conn.transport) {
|
|
events.emit(this, "garbage-collection", data);
|
|
}
|
|
},
|
|
|
|
_onAllocations: function (data) {
|
|
if (this.conn.transport) {
|
|
events.emit(this, "allocations", data);
|
|
}
|
|
},
|
|
});
|
|
|
|
exports.MemoryFront = protocol.FrontClass(MemoryActor, {
|
|
initialize: function(client, form, rootForm = null) {
|
|
protocol.Front.prototype.initialize.call(this, client, form);
|
|
this._client = client;
|
|
this.actorID = form.memoryActor;
|
|
this.heapSnapshotFileActorID = rootForm
|
|
? rootForm.heapSnapshotFileActor
|
|
: null;
|
|
this.manage(this);
|
|
},
|
|
|
|
/**
|
|
* Save a heap snapshot, transfer it from the server to the client if the
|
|
* server and client do not share a file system, and return the local file
|
|
* path to the heap snapshot.
|
|
*
|
|
* Note that this is safe to call for actors inside sandoxed child processes,
|
|
* as we jump through the correct IPDL hoops.
|
|
*
|
|
* @params Boolean options.forceCopy
|
|
* Always force a bulk data copy of the saved heap snapshot, even when
|
|
* the server and client share a file system.
|
|
*
|
|
* @returns Promise<String>
|
|
*/
|
|
saveHeapSnapshot: protocol.custom(Task.async(function* (options = {}) {
|
|
const snapshotId = yield this._saveHeapSnapshotImpl();
|
|
|
|
if (!options.forceCopy &&
|
|
(yield HeapSnapshotFileUtils.haveHeapSnapshotTempFile(snapshotId))) {
|
|
return HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId);
|
|
}
|
|
|
|
return yield this.transferHeapSnapshot(snapshotId);
|
|
}), {
|
|
impl: "_saveHeapSnapshotImpl"
|
|
}),
|
|
|
|
/**
|
|
* Given that we have taken a heap snapshot with the given id, transfer the
|
|
* heap snapshot file to the client. The path to the client's local file is
|
|
* returned.
|
|
*
|
|
* @param {String} snapshotId
|
|
*
|
|
* @returns Promise<String>
|
|
*/
|
|
transferHeapSnapshot: protocol.custom(function (snapshotId) {
|
|
if (!this.heapSnapshotFileActorID) {
|
|
throw new Error("MemoryFront initialized without a rootForm");
|
|
}
|
|
|
|
const request = this._client.request({
|
|
to: this.heapSnapshotFileActorID,
|
|
type: "transferHeapSnapshot",
|
|
snapshotId
|
|
});
|
|
|
|
return new Promise((resolve, reject) => {
|
|
const outFilePath =
|
|
HeapSnapshotFileUtils.getNewUniqueHeapSnapshotTempFilePath();
|
|
const outFile = new FileUtils.File(outFilePath);
|
|
|
|
const outFileStream = FileUtils.openSafeFileOutputStream(outFile);
|
|
request.on("bulk-reply", Task.async(function* ({ copyTo }) {
|
|
yield copyTo(outFileStream);
|
|
FileUtils.closeSafeFileOutputStream(outFileStream);
|
|
resolve(outFilePath);
|
|
}));
|
|
});
|
|
})
|
|
});
|