summaryrefslogtreecommitdiffstats
path: root/devtools/shared/heapsnapshot/HeapAnalysesClient.js
blob: 278b591247530a0453faae23d84ab5eb07f10d3c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
/* 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 DevToolsUtils = require("resource://devtools/shared/DevToolsUtils.js");
const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
  DevToolsWorker: "resource://devtools/shared/worker/worker.sys.mjs",
});

const WORKER_URL =
  "resource://devtools/shared/heapsnapshot/HeapAnalyses.worker.js";
var workerCounter = 0;

/**
 * A HeapAnalysesClient instance provides a developer-friendly interface for
 * interacting with a HeapAnalysesWorker. This enables users to be ignorant of
 * the message passing protocol used to communicate with the worker. The
 * HeapAnalysesClient owns the worker, and terminating the worker is done by
 * terminating the client (see the `destroy` method).
 */
const HeapAnalysesClient = (module.exports = function () {
  this._worker = new lazy.DevToolsWorker(WORKER_URL, {
    name: `HeapAnalyses-${workerCounter++}`,
    verbose: DevToolsUtils.dumpv.wantVerbose,
  });
});

/**
 * Destroy the worker, causing it to release its resources (such as heap
 * snapshots it has deserialized and read into memory). The client is no longer
 * usable after calling this method.
 */
HeapAnalysesClient.prototype.destroy = function () {
  this._worker.destroy();
  this._worker = null;
};

/**
 * Tell the worker to read into memory the heap snapshot at the given file
 * path. This is a prerequisite for asking the worker to perform various
 * analyses on a heap snapshot.
 *
 * @param {String} snapshotFilePath
 *
 * @returns Promise
 *          The promise is fulfilled if the heap snapshot is successfully
 *          deserialized and read into memory. The promise is rejected if that
 *          does not happen, eg due to a bad file path or malformed heap
 *          snapshot file.
 */
HeapAnalysesClient.prototype.readHeapSnapshot = function (snapshotFilePath) {
  return this._worker.performTask("readHeapSnapshot", { snapshotFilePath });
};

/**
 * Tell the worker to delete all references to the snapshot and dominator trees
 * linked to the provided snapshot file path.
 *
 * @param {String} snapshotFilePath
 * @return Promise<undefined>
 */
HeapAnalysesClient.prototype.deleteHeapSnapshot = function (snapshotFilePath) {
  return this._worker.performTask("deleteHeapSnapshot", { snapshotFilePath });
};

/**
 * Request the creation time given a snapshot file path. Returns `null`
 * if snapshot does not exist.
 *
 * @param {String} snapshotFilePath
 *        The path to the snapshot.
 * @return {Number?}
 *        The unix timestamp of the creation time of the snapshot, or null if
 *        snapshot does not exist.
 */
HeapAnalysesClient.prototype.getCreationTime = function (snapshotFilePath) {
  return this._worker.performTask("getCreationTime", snapshotFilePath);
};

/** * Censuses *****************************************************************/

/**
 * Ask the worker to perform a census analysis on the heap snapshot with the
 * given path. The heap snapshot at the given path must have already been read
 * into memory by the worker (see `readHeapSnapshot`).
 *
 * @param {String} snapshotFilePath
 *
 * @param {Object} censusOptions
 *        A structured-cloneable object specifying the requested census's
 *        breakdown. See the "takeCensus" section of
 *        `js/src/doc/Debugger/Debugger.Memory.md` for detailed documentation.
 *
 * @param {Object} requestOptions
 *        An object specifying options of this worker request.
 *        - {Boolean} asTreeNode
 *          Whether or not the census is returned as a CensusTreeNode,
 *          or just a breakdown report. Defaults to false.
 *          @see `devtools/shared/heapsnapshot/census-tree-node.js`
 *        - {Boolean} asInvertedTreeNode
 *          Whether or not the census is returned as an inverted
 *          CensusTreeNode. Defaults to false.
 *        - {String} filter
 *          A filter string to prune the resulting tree with. Only applies if
 *          either asTreeNode or asInvertedTreeNode is true.
 *
 * @returns Promise<Object>
 *          An object with the following properties:
 *          - report:
 *            The report generated by the given census breakdown, or a
 *            CensusTreeNode generated by the given census breakdown if
 *            `asTreeNode` is true.
 *          - parentMap:
 *            The result of calling CensusUtils.createParentMap on the generated
 *            report. Only exists if asTreeNode or asInvertedTreeNode are set.
 */
HeapAnalysesClient.prototype.takeCensus = function (
  snapshotFilePath,
  censusOptions,
  requestOptions = {}
) {
  return this._worker.performTask("takeCensus", {
    snapshotFilePath,
    censusOptions,
    requestOptions,
  });
};

/**
 * Get the individual nodes that correspond to the given census report leaf
 * indices.
 *
 * @param {Object} opts
 *        An object with the following properties:
 *        - {DominatorTreeId} dominatorTreeId: The id of the dominator tree.
 *        - {Set<Number>} indices: The indices of the census report leaves we
 *          would like to get the individuals for.
 *        - {Object} censusBreakdown: The breakdown used to generate the census.
 *        - {Object} labelBreakdown: The breakdown we would like to use when
 *          labeling the resulting nodes.
 *        - {Number} maxRetainingPaths: The maximum number of retaining paths to
 *          compute for each node.
 *        - {Number} maxIndividuals: The maximum number of individual nodes to
 *          return.
 *
 * @returns {Promise<Object>}
 *          A promise of an object with the following properties:
 *          - {Array<DominatorTreeNode>} nodes: An array of `DominatorTreeNode`s
 *            with their shortest paths attached, and without any dominator tree
 *            child/parent information attached. The results are sorted by
 *            retained size.
 *
 */
HeapAnalysesClient.prototype.getCensusIndividuals = function (opts) {
  return this._worker.performTask("getCensusIndividuals", opts);
};

/**
 * Request that the worker take a census on the heap snapshots with the given
 * paths and then return the difference between them. Both heap snapshots must
 * have already been read into memory by the worker (see `readHeapSnapshot`).
 *
 * @param {String} firstSnapshotFilePath
 *        The first snapshot file path.
 *
 * @param {String} secondSnapshotFilePath
 *        The second snapshot file path.
 *
 * @param {Object} censusOptions
 *        A structured-cloneable object specifying the requested census's
 *        breakdown. See the "takeCensus" section of
 *        `js/src/doc/Debugger/Debugger.Memory.md` for detailed documentation.
 *
 * @param {Object} requestOptions
 *        An object specifying options for this request.
 *        - {Boolean} asTreeNode
 *          Whether the resulting delta report should be converted to a census
 *          tree node before returned. Defaults to false.
 *        - {Boolean} asInvertedTreeNode
 *          Whether or not the census is returned as an inverted
 *          CensusTreeNode. Defaults to false.
 *        - {String} filter
 *          A filter string to prune the resulting tree with. Only applies if
 *          either asTreeNode or asInvertedTreeNode is true.
 *
 * @returns Promise<Object>
 *          - delta:
 *            The delta report generated by diffing the two census reports, or a
 *            CensusTreeNode generated from the delta report if
 *            `requestOptions.asTreeNode` was true.
 *          - parentMap:
 *            The result of calling CensusUtils.createParentMap on the generated
 *            delta. Only exists if asTreeNode or asInvertedTreeNode are set.
 */
HeapAnalysesClient.prototype.takeCensusDiff = function (
  firstSnapshotFilePath,
  secondSnapshotFilePath,
  censusOptions,
  requestOptions = {}
) {
  return this._worker.performTask("takeCensusDiff", {
    firstSnapshotFilePath,
    secondSnapshotFilePath,
    censusOptions,
    requestOptions,
  });
};

/** * Dominator Trees **********************************************************/

/**
 * Compute the dominator tree of the heap snapshot loaded from the given file
 * path. Returns the id of the computed dominator tree.
 *
 * @param {String} snapshotFilePath
 *
 * @returns {Promise<DominatorTreeId>}
 */
HeapAnalysesClient.prototype.computeDominatorTree = function (
  snapshotFilePath
) {
  return this._worker.performTask("computeDominatorTree", snapshotFilePath);
};

/**
 * Get the initial, partial view of the dominator tree with the given id.
 *
 * @param {Object} opts
 *        An object specifying options for this request.
 *        - {DominatorTreeId} dominatorTreeId
 *          The id of the dominator tree.
 *        - {Object} breakdown
 *          The breakdown used to generate node labels.
 *        - {Number} maxDepth
 *          The maximum depth to traverse down the tree to create this initial
 *          view.
 *        - {Number} maxSiblings
 *          The maximum number of siblings to visit within each traversed node's
 *          children.
 *        - {Number} maxRetainingPaths
 *          The maximum number of retaining paths to find for each node.
 *
 * @returns {Promise<DominatorTreeNode>}
 */
HeapAnalysesClient.prototype.getDominatorTree = function (opts) {
  return this._worker.performTask("getDominatorTree", opts);
};

/**
 * Get a subset of a nodes children in the dominator tree.
 *
 * @param {Object} opts
 *        An object specifying options for this request.
 *        - {DominatorTreeId} dominatorTreeId
 *          The id of the dominator tree.
 *        - {NodeId} nodeId
 *          The id of the node whose children are being found.
 *        - {Object} breakdown
 *          The breakdown used to generate node labels.
 *        - {Number} startIndex
 *          The starting index within the full set of immediately dominated
 *          children of the children being requested. Children are always sorted
 *          by greatest to least retained size.
 *        - {Number} maxCount
 *          The maximum number of children to return.
 *        - {Number} maxRetainingPaths
 *          The maximum number of retaining paths to find for each node.
 *
 * @returns {Promise<Object>}
 *          A promise of an object with the following properties:
 *          - {Array<DominatorTreeNode>} nodes
 *            The requested nodes that are immediately dominated by the node
 *            identified by `opts.nodeId`.
 *          - {Boolean} moreChildrenAvailable
 *            True iff there are more children available after the returned
 *            nodes.
 *          - {Array<NodeId>} path
 *            The path through the tree from the root to these node's parent, eg
 *            [root's id, child of root's id, child of child of root's id, ..., `nodeId`].
 */
HeapAnalysesClient.prototype.getImmediatelyDominated = function (opts) {
  return this._worker.performTask("getImmediatelyDominated", opts);
};