memory.js (3653B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 "use strict"; 6 7 const { memorySpec } = require("resource://devtools/shared/specs/memory.js"); 8 const { 9 FrontClassWithSpec, 10 registerFront, 11 } = require("resource://devtools/shared/protocol.js"); 12 13 const lazy = {}; 14 ChromeUtils.defineESModuleGetters(lazy, { 15 FileUtils: "resource://gre/modules/FileUtils.sys.mjs", 16 }); 17 loader.lazyRequireGetter( 18 this, 19 "HeapSnapshotFileUtils", 20 "resource://devtools/shared/heapsnapshot/HeapSnapshotFileUtils.js" 21 ); 22 23 class MemoryFront extends FrontClassWithSpec(memorySpec) { 24 constructor(client, targetFront, parentFront) { 25 super(client, targetFront, parentFront); 26 this._client = client; 27 28 // Attribute name from which to retrieve the actorID out of the target actor's form 29 this.formAttributeName = "memoryActor"; 30 } 31 32 /** 33 * Save a heap snapshot, transfer it from the server to the client if the 34 * server and client do not share a file system, and return the local file 35 * path to the heap snapshot. 36 * 37 * Note that this is safe to call for actors inside sandoxed child processes, 38 * as we jump through the correct IPDL hoops. 39 * 40 * @param {object} options 41 * @param {boolean} options.forceCopy 42 * Always force a bulk data copy of the saved heap snapshot, even when 43 * the server and client share a file system. 44 * 45 * @param {object | undefined} options.boundaries 46 * The boundaries for the heap snapshot. See 47 * ChromeUtils.webidl for more details. 48 * 49 * @returns Promise<String> 50 */ 51 async saveHeapSnapshot(options = {}) { 52 const snapshotId = await super.saveHeapSnapshot(options.boundaries); 53 54 if ( 55 !options.forceCopy && 56 (await HeapSnapshotFileUtils.haveHeapSnapshotTempFile(snapshotId)) 57 ) { 58 return HeapSnapshotFileUtils.getHeapSnapshotTempFilePath(snapshotId); 59 } 60 61 return this.transferHeapSnapshot(snapshotId); 62 } 63 64 /** 65 * Given that we have taken a heap snapshot with the given id, transfer the 66 * heap snapshot file to the client. The path to the client's local file is 67 * returned. 68 * 69 * @param {string} snapshotId 70 * 71 * @returns Promise<String> 72 */ 73 async transferHeapSnapshot(snapshotId) { 74 const heapSnapshotFileFront = 75 await this._client.mainRoot.getFront("heapSnapshotFile"); 76 77 try { 78 const request = heapSnapshotFileFront.transferHeapSnapshot(snapshotId); 79 80 const outFilePath = 81 HeapSnapshotFileUtils.getNewUniqueHeapSnapshotTempFilePath(); 82 const outFile = new lazy.FileUtils.File(outFilePath); 83 const outFileStream = lazy.FileUtils.openSafeFileOutputStream(outFile); 84 85 // This request is a bulk request. That's why the result of the request is 86 // an object with the `copyTo` function that can transfer the data to 87 // another stream. 88 // See devtools/shared/transport/transport.js to know more about this mode. 89 const { copyTo } = await request; 90 await copyTo(outFileStream); 91 92 lazy.FileUtils.closeSafeFileOutputStream(outFileStream); 93 return outFilePath; 94 } catch (e) { 95 if (e.error) { 96 // This isn't a real error, rather this is a message coming from the 97 // server. So let's throw a real error instead. 98 throw new Error( 99 `The server's actor threw an error: (${e.error}) ${e.message}` 100 ); 101 } 102 103 // Otherwise, rethrow the error 104 throw e; 105 } 106 } 107 } 108 109 exports.MemoryFront = MemoryFront; 110 registerFront(MemoryFront);