summaryrefslogtreecommitdiffstats
path: root/devtools/client/fronts/memory.js
blob: 80ba417b67f041ab91412f10e62d47c3b92b39aa (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
/* 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 { memorySpec } = require("resource://devtools/shared/specs/memory.js");
const {
  FrontClassWithSpec,
  registerFront,
} = require("resource://devtools/shared/protocol.js");

const lazy = {};
ChromeUtils.defineESModuleGetters(lazy, {
  FileUtils: "resource://gre/modules/FileUtils.sys.mjs",
});
loader.lazyRequireGetter(
  this,
  "HeapSnapshotFileUtils",
  "resource://devtools/shared/heapsnapshot/HeapSnapshotFileUtils.js"
);

class MemoryFront extends FrontClassWithSpec(memorySpec) {
  constructor(client, targetFront, parentFront) {
    super(client, targetFront, parentFront);
    this._client = client;
    this.heapSnapshotFileActorID = null;

    // Attribute name from which to retrieve the actorID out of the target actor's form
    this.formAttributeName = "memoryActor";
  }

  /**
   * 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.
   *
   * @params {Object|undefined} options.boundaries
   *         The boundaries for the heap snapshot. See
   *         ChromeUtils.webidl for more details.
   *
   * @returns Promise<String>
   */
  async saveHeapSnapshot(options = {}) {
    const snapshotId = await super.saveHeapSnapshot(options.boundaries);

    if (
      !options.forceCopy &&
      (await HeapSnapshotFileUtils.haveHeapSnapshotTempFile(snapshotId))
    ) {
      return HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId);
    }

    return this.transferHeapSnapshot(snapshotId);
  }

  /**
   * 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>
   */
  async transferHeapSnapshot(snapshotId) {
    if (!this.heapSnapshotFileActorID) {
      const form = await this._client.mainRoot.rootForm;
      this.heapSnapshotFileActorID = form.heapSnapshotFileActor;
    }

    try {
      const request = this._client.request({
        to: this.heapSnapshotFileActorID,
        type: "transferHeapSnapshot",
        snapshotId,
      });

      const outFilePath =
        HeapSnapshotFileUtils.getNewUniqueHeapSnapshotTempFilePath();
      const outFile = new lazy.FileUtils.File(outFilePath);
      const outFileStream = lazy.FileUtils.openSafeFileOutputStream(outFile);

      // This request is a bulk request. That's why the result of the request is
      // an object with the `copyTo` function that can transfer the data to
      // another stream.
      // See devtools/shared/transport/transport.js to know more about this mode.
      const { copyTo } = await request;
      await copyTo(outFileStream);

      lazy.FileUtils.closeSafeFileOutputStream(outFileStream);
      return outFilePath;
    } catch (e) {
      if (e.error) {
        // This isn't a real error, rather this is a message coming from the
        // server. So let's throw a real error instead.
        throw new Error(
          `The server's actor threw an error: (${e.error}) ${e.message}`
        );
      }

      // Otherwise, rethrow the error
      throw e;
    }
  }
}

exports.MemoryFront = MemoryFront;
registerFront(MemoryFront);