mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1201213 - Add a HeapAnalyses{Worker,Client} for running heap analyses; r=jsantell
This commit is contained in:
parent
4ed9ec0501
commit
1e3a3a0f76
74
toolkit/devtools/heapsnapshot/HeapAnalysesClient.js
Normal file
74
toolkit/devtools/heapsnapshot/HeapAnalysesClient.js
Normal file
@ -0,0 +1,74 @@
|
||||
/* 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("devtools/toolkit/DevToolsUtils");
|
||||
const { DevToolsWorker } = require("devtools/toolkit/shared/worker.js");
|
||||
|
||||
const WORKER_URL = "resource://gre/modules/devtools/heapsnapshot/HeapAnalysesWorker.js";
|
||||
let 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 DevToolsWorker(WORKER_URL, {
|
||||
name: `HeapAnalyses-${workerCounter++}`,
|
||||
verbose: DevToolsUtils.dumpn.wantLogging
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 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();
|
||||
};
|
||||
|
||||
/**
|
||||
* 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 });
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @returns Promise<census report>
|
||||
* The report generated by the given census breakdown.
|
||||
*/
|
||||
HeapAnalysesClient.prototype.takeCensus = function (snapshotFilePath,
|
||||
censusOptions) {
|
||||
return this._worker.performTask("takeCensus", {
|
||||
snapshotFilePath,
|
||||
censusOptions
|
||||
});
|
||||
};
|
37
toolkit/devtools/heapsnapshot/HeapAnalysesWorker.js
Normal file
37
toolkit/devtools/heapsnapshot/HeapAnalysesWorker.js
Normal file
@ -0,0 +1,37 @@
|
||||
/* 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/. */
|
||||
/*global ThreadSafeChromeUtils*/
|
||||
|
||||
// This is a worker which reads offline heap snapshots into memory and performs
|
||||
// heavyweight analyses on them without blocking the main thread. A
|
||||
// HeapAnalysesWorker is owned and communicated with by a HeapAnalysesClient
|
||||
// instance. See HeapAnalysesClient.js.
|
||||
|
||||
"use strict";
|
||||
|
||||
importScripts("resource://gre/modules/devtools/shared/worker-helper.js");
|
||||
|
||||
// The set of HeapSnapshot instances this worker has read into memory. Keyed by
|
||||
// snapshot file path.
|
||||
const snapshots = Object.create(null);
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.readHeapSnapshot
|
||||
*/
|
||||
workerHelper.createTask(self, "readHeapSnapshot", ({ snapshotFilePath }) => {
|
||||
snapshots[snapshotFilePath] =
|
||||
ThreadSafeChromeUtils.readHeapSnapshot(snapshotFilePath);
|
||||
return true;
|
||||
});
|
||||
|
||||
/**
|
||||
* @see HeapAnalysesClient.prototype.takeCensus
|
||||
*/
|
||||
workerHelper.createTask(self, "takeCensus", ({ snapshotFilePath, censusOptions }) => {
|
||||
if (!snapshots[snapshotFilePath]) {
|
||||
throw new Error(`No known heap snapshot for '${snapshotFilePath}'`);
|
||||
}
|
||||
|
||||
return snapshots[snapshotFilePath].takeCensus(censusOptions);
|
||||
});
|
@ -32,5 +32,6 @@ DEFINES['GOOGLE_PROTOBUF_NO_RTTI'] = True
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
EXTRA_JS_MODULES.devtools.heapsnapshot += [
|
||||
'HeapSnapshotFileUtils.js',
|
||||
'HeapAnalysesClient.js',
|
||||
'HeapAnalysesWorker.js',
|
||||
]
|
||||
|
@ -14,12 +14,17 @@ const { Match } = Cu.import("resource://test/Match.jsm", {});
|
||||
const { Census } = Cu.import("resource://test/Census.jsm", {});
|
||||
const { addDebuggerToGlobal } =
|
||||
Cu.import("resource://gre/modules/jsdebugger.jsm", {});
|
||||
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const HeapAnalysesClient =
|
||||
require("devtools/toolkit/heapsnapshot/HeapAnalysesClient");
|
||||
const Services = require("Services");
|
||||
|
||||
// Always log packets when running tests. runxpcshelltests.py will throw
|
||||
// the output away anyway, unless you give it the --verbose flag.
|
||||
Services.prefs.setBoolPref("devtools.debugger.log", true);
|
||||
DevToolsUtils.dumpn.wantLogging = true;
|
||||
|
||||
const SYSTEM_PRINCIPAL = Cc["@mozilla.org/systemprincipal;1"]
|
||||
.createInstance(Ci.nsIPrincipal);
|
||||
@ -100,6 +105,16 @@ function getFilePath(aName, aAllowMissing=false, aUsePlatformPathSeparator=false
|
||||
return path;
|
||||
}
|
||||
|
||||
function saveNewHeapSnapshot(fileName=`core-dump-${Math.random()}.tmp`) {
|
||||
const filePath = getFilePath(fileName, true, true);
|
||||
ok(filePath, "Should get a file path to save the core dump to.");
|
||||
|
||||
ChromeUtils.saveHeapSnapshot(filePath, { runtime: true });
|
||||
ok(true, "Saved a heap snapshot to " + filePath);
|
||||
|
||||
return filePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save a heap snapshot to the file with the given name in the current
|
||||
* directory, read it back as a HeapSnapshot instance, and then take a census of
|
||||
|
@ -0,0 +1,18 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the HeapAnalyses{Client,Worker} can read heap snapshots.
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const client = new HeapAnalysesClient();
|
||||
|
||||
const snapshotFilePath = saveNewHeapSnapshot();
|
||||
yield client.readHeapSnapshot(snapshotFilePath);
|
||||
ok(true, "Should have read the heap snapshot");
|
||||
|
||||
client.destroy();
|
||||
});
|
@ -0,0 +1,27 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the HeapAnalyses{Client,Worker} can take censuses.
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const client = new HeapAnalysesClient();
|
||||
|
||||
const snapshotFilePath = saveNewHeapSnapshot();
|
||||
yield client.readHeapSnapshot(snapshotFilePath);
|
||||
ok(true, "Should have read the heap snapshot");
|
||||
|
||||
const report = yield client.takeCensus(snapshotFilePath);
|
||||
ok(report, "Should get a report");
|
||||
equal(typeof report, "object", "report should be an object");
|
||||
|
||||
ok(report.objects);
|
||||
ok(report.scripts);
|
||||
ok(report.strings);
|
||||
ok(report.other);
|
||||
|
||||
client.destroy();
|
||||
});
|
@ -0,0 +1,29 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the HeapAnalyses{Client,Worker} can take censuses with breakdown
|
||||
// options.
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const client = new HeapAnalysesClient();
|
||||
|
||||
const snapshotFilePath = saveNewHeapSnapshot();
|
||||
yield client.readHeapSnapshot(snapshotFilePath);
|
||||
ok(true, "Should have read the heap snapshot");
|
||||
|
||||
const report = yield client.takeCensus(snapshotFilePath, {
|
||||
breakdown: { by: "count", count: true, bytes: true }
|
||||
});
|
||||
|
||||
ok(report, "Should get a report");
|
||||
equal(typeof report, "object", "report should be an object");
|
||||
|
||||
ok(report.count);
|
||||
ok(report.bytes);
|
||||
|
||||
client.destroy();
|
||||
});
|
@ -0,0 +1,48 @@
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test that the HeapAnalyses{Client,Worker} bubbles errors properly when things
|
||||
// go wrong.
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
add_task(function* () {
|
||||
const client = new HeapAnalysesClient();
|
||||
|
||||
// Snapshot file path to a file that doesn't exist.
|
||||
let failed = false;
|
||||
try {
|
||||
yield client.readHeapSnapshot(getFilePath("foo-bar-baz" + Math.random(), true));
|
||||
} catch (e) {
|
||||
failed = true;
|
||||
}
|
||||
ok(failed, "should not read heap snapshots that do not exist");
|
||||
|
||||
// Snapshot file path to a file that is not a heap snapshot.
|
||||
failed = false;
|
||||
try {
|
||||
yield client.readHeapSnapshot(getFilePath("test_HeapAnalyses_takeCensus_03.js"));
|
||||
} catch (e) {
|
||||
failed = true;
|
||||
}
|
||||
ok(failed, "should not be able to read a file that is not a heap snapshot as a heap snapshot");
|
||||
|
||||
const snapshotFilePath = saveNewHeapSnapshot();
|
||||
yield client.readHeapSnapshot(snapshotFilePath);
|
||||
ok(true, "Should have read the heap snapshot");
|
||||
|
||||
// Bad census breakdown options.
|
||||
failed = false;
|
||||
try {
|
||||
yield client.takeCensus(snapshotFilePath, {
|
||||
breakdown: { by: "some classification that we do not have" }
|
||||
});
|
||||
} catch (e) {
|
||||
failed = true;
|
||||
}
|
||||
ok(failed, "should not be able to breakdown by an unknown classification");
|
||||
|
||||
client.destroy();
|
||||
});
|
@ -9,6 +9,10 @@ support-files =
|
||||
heap-snapshot-worker.js
|
||||
Match.jsm
|
||||
|
||||
[test_HeapAnalyses_readHeapSnapshot_01.js]
|
||||
[test_HeapAnalyses_takeCensus_01.js]
|
||||
[test_HeapAnalyses_takeCensus_02.js]
|
||||
[test_HeapAnalyses_takeCensus_03.js]
|
||||
[test_HeapSnapshot_takeCensus_01.js]
|
||||
[test_HeapSnapshot_takeCensus_02.js]
|
||||
[test_HeapSnapshot_takeCensus_03.js]
|
||||
|
Loading…
Reference in New Issue
Block a user