mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-02 15:15:23 +00:00
171 lines
5.4 KiB
JavaScript
171 lines
5.4 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, Cr } = require("chrome");
|
|
|
|
loader.lazyRequireGetter(this, "Services");
|
|
loader.lazyRequireGetter(this, "promise");
|
|
loader.lazyRequireGetter(this, "RecordingUtils",
|
|
"devtools/shared/performance/recording-utils");
|
|
|
|
loader.lazyImporter(this, "FileUtils",
|
|
"resource://gre/modules/FileUtils.jsm");
|
|
loader.lazyImporter(this, "NetUtil",
|
|
"resource://gre/modules/NetUtil.jsm");
|
|
|
|
// This identifier string is used to tentatively ascertain whether or not
|
|
// a JSON loaded from disk is actually something generated by this tool.
|
|
// It isn't, of course, a definitive verification, but a Good Enough™
|
|
// approximation before continuing the import. Don't localize this.
|
|
const PERF_TOOL_SERIALIZER_IDENTIFIER = "Recorded Performance Data";
|
|
const PERF_TOOL_SERIALIZER_LEGACY_VERSION = 1;
|
|
const PERF_TOOL_SERIALIZER_CURRENT_VERSION = 2;
|
|
|
|
/**
|
|
* Helpers for importing/exporting JSON.
|
|
*/
|
|
|
|
/**
|
|
* Gets a nsIScriptableUnicodeConverter instance with a default UTF-8 charset.
|
|
* @return object
|
|
*/
|
|
function getUnicodeConverter () {
|
|
let className = "@mozilla.org/intl/scriptableunicodeconverter";
|
|
let converter = Cc[className].createInstance(Ci.nsIScriptableUnicodeConverter);
|
|
converter.charset = "UTF-8";
|
|
return converter;
|
|
}
|
|
|
|
/**
|
|
* Saves a recording as JSON to a file. The provided data is assumed to be
|
|
* acyclical, so that it can be properly serialized.
|
|
*
|
|
* @param object recordingData
|
|
* The recording data to stream as JSON.
|
|
* @param nsILocalFile file
|
|
* The file to stream the data into.
|
|
* @return object
|
|
* A promise that is resolved once streaming finishes, or rejected
|
|
* if there was an error.
|
|
*/
|
|
function saveRecordingToFile (recordingData, file) {
|
|
let deferred = promise.defer();
|
|
|
|
recordingData.fileType = PERF_TOOL_SERIALIZER_IDENTIFIER;
|
|
recordingData.version = PERF_TOOL_SERIALIZER_CURRENT_VERSION;
|
|
|
|
let string = JSON.stringify(recordingData);
|
|
let inputStream = this.getUnicodeConverter().convertToInputStream(string);
|
|
let outputStream = FileUtils.openSafeFileOutputStream(file);
|
|
|
|
NetUtil.asyncCopy(inputStream, outputStream, deferred.resolve);
|
|
return deferred.promise;
|
|
}
|
|
|
|
/**
|
|
* Loads a recording stored as JSON from a file.
|
|
*
|
|
* @param nsILocalFile file
|
|
* The file to import the data from.
|
|
* @return object
|
|
* A promise that is resolved once importing finishes, or rejected
|
|
* if there was an error.
|
|
*/
|
|
function loadRecordingFromFile (file) {
|
|
let deferred = promise.defer();
|
|
|
|
let channel = NetUtil.newChannel({
|
|
uri: NetUtil.newURI(file),
|
|
loadUsingSystemPrincipal: true});
|
|
|
|
channel.contentType = "text/plain";
|
|
|
|
NetUtil.asyncFetch(channel, (inputStream, status) => {
|
|
try {
|
|
let string = NetUtil.readInputStreamToString(inputStream, inputStream.available());
|
|
var recordingData = JSON.parse(string);
|
|
} catch (e) {
|
|
deferred.reject(new Error("Could not read recording data file."));
|
|
return;
|
|
}
|
|
if (recordingData.fileType != PERF_TOOL_SERIALIZER_IDENTIFIER) {
|
|
deferred.reject(new Error("Unrecognized recording data file."));
|
|
return;
|
|
}
|
|
if (!isValidSerializerVersion(recordingData.version)) {
|
|
deferred.reject(new Error("Unsupported recording data file version."));
|
|
return;
|
|
}
|
|
if (recordingData.version === PERF_TOOL_SERIALIZER_LEGACY_VERSION) {
|
|
recordingData = convertLegacyData(recordingData);
|
|
}
|
|
if (recordingData.profile.meta.version === 2) {
|
|
RecordingUtils.deflateProfile(recordingData.profile);
|
|
}
|
|
if (!recordingData.label) {
|
|
// set the label to the filename without its extension
|
|
recordingData.label = file.leafName.replace(/\..+$/, "");
|
|
}
|
|
deferred.resolve(recordingData);
|
|
});
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
/**
|
|
* Returns a boolean indicating whether or not the passed in `version`
|
|
* is supported by this serializer.
|
|
*
|
|
* @param number version
|
|
* @return boolean
|
|
*/
|
|
function isValidSerializerVersion (version) {
|
|
return !!~[
|
|
PERF_TOOL_SERIALIZER_LEGACY_VERSION,
|
|
PERF_TOOL_SERIALIZER_CURRENT_VERSION
|
|
].indexOf(version);
|
|
}
|
|
|
|
/**
|
|
* Takes recording data (with version `1`, from the original profiler tool), and
|
|
* massages the data to be line with the current performance tool's property names
|
|
* and values.
|
|
*
|
|
* @param object legacyData
|
|
* @return object
|
|
*/
|
|
function convertLegacyData (legacyData) {
|
|
let { profilerData, ticksData, recordingDuration } = legacyData;
|
|
|
|
// The `profilerData` and `ticksData` stay, but the previously unrecorded
|
|
// fields just are empty arrays or objects.
|
|
let data = {
|
|
label: profilerData.profilerLabel,
|
|
duration: recordingDuration,
|
|
markers: [],
|
|
frames: [],
|
|
memory: [],
|
|
ticks: ticksData,
|
|
allocations: { sites: [], timestamps: [], frames: [], sizes: [] },
|
|
profile: profilerData.profile,
|
|
// Fake a configuration object here if there's tick data,
|
|
// so that it can be rendered
|
|
configuration: {
|
|
withTicks: !!ticksData.length,
|
|
withMarkers: false,
|
|
withMemory: false,
|
|
withAllocations: false
|
|
},
|
|
systemHost: {},
|
|
systemClient: {},
|
|
};
|
|
|
|
return data;
|
|
}
|
|
|
|
exports.getUnicodeConverter = getUnicodeConverter;
|
|
exports.saveRecordingToFile = saveRecordingToFile;
|
|
exports.loadRecordingFromFile = loadRecordingFromFile;
|