Loading browser/devtools/performance/modules/front.js +30 −59 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ let SharedPerformanceActors = new WeakMap(); * * @param Target target * The target owning this connection. * @return PerformanceActorsConnection * The shared connection for the specified target. */ SharedPerformanceActors.forTarget = function(target) { if (this.has(target)) { Loading Loading @@ -140,12 +142,13 @@ PerformanceActorsConnection.prototype = { this._timeline = new TimelineFront(this._target.client, this._target.form); } else { this._timeline = { start: () => {}, stop: () => {}, start: () => 0, stop: () => 0, isRecording: () => false, on: () => {}, off: () => {}, destroy: () => {} on: () => null, off: () => null, once: () => promise.reject(), destroy: () => null }; } }, Loading Loading @@ -211,36 +214,41 @@ PerformanceFront.prototype = { /** * Manually begins a recording session. * * @param object options * @param object timelineOptions * An options object to pass to the timeline front. Supported * properties are `withTicks` and `withMemory`. * @return object * A promise that is resolved once recording has started. */ startRecording: Task.async(function*(options = {}) { let { isActive, currentTime } = yield this._request("profiler", "isActive"); startRecording: Task.async(function*(timelineOptions = {}) { let profilerStatus = yield this._request("profiler", "isActive"); let profilerStartTime; // Start the profiler only if it wasn't already active. The built-in // nsIPerformance module will be kept recording, because it's the same instance // for all targets and interacts with the whole platform, so we don't want // to affect other clients by stopping (or restarting) it. if (!isActive) { if (!profilerStatus.isActive) { // Extend the profiler options so that protocol.js doesn't modify the original. let profilerOptions = extend({}, this._customProfilerOptions); yield this._request("profiler", "startProfiler", profilerOptions); this._profilingStartTime = 0; profilerStartTime = 0; this.emit("profiler-activated"); } else { this._profilingStartTime = currentTime; profilerStartTime = profilerStatus.currentTime; this.emit("profiler-already-active"); } // The timeline actor is target-dependent, so just make sure // it's recording. let startTime = yield this._request("timeline", "start", options); // The timeline actor is target-dependent, so just make sure it's recording. // It won't, however, be available in older Geckos (FF < 35). let timelineStartTime = yield this._request("timeline", "start", timelineOptions); // Return only the start time from the timeline actor. return { startTime }; // Return the start times from the two actors. They will be used to // synchronize the profiler and timeline data. return { profilerStartTime, timelineStartTime }; }), /** Loading @@ -251,19 +259,15 @@ PerformanceFront.prototype = { * with the profiler and timeline data. */ stopRecording: Task.async(function*() { // We'll need to filter out all samples that fall out of current profile's // range. This is necessary because the profiler is continuously running. let timelineEndTime = yield this._request("timeline", "stop"); let profilerData = yield this._request("profiler", "getProfile"); filterSamples(profilerData, this._profilingStartTime); offsetSampleTimes(profilerData, this._profilingStartTime); let endTime = yield this._request("timeline", "stop"); // Join all the acquired data and return it for outside consumers. // Return the end times from the two actors. They will be used to // synchronize the profiler and timeline data. return { recordingDuration: profilerData.currentTime - this._profilingStartTime, profilerData: profilerData, endTime: endTime profile: profilerData.profile, profilerEndTime: profilerData.currentTime, timelineEndTime: timelineEndTime }; }), Loading @@ -280,39 +284,6 @@ PerformanceFront.prototype = { } }; /** * Filters all the samples in the provided profiler data to be more recent * than the specified start time. * * @param object profilerData * The profiler data received from the backend. * @param number profilingStartTime * The earliest acceptable sample time (in milliseconds). */ function filterSamples(profilerData, profilingStartTime) { let firstThread = profilerData.profile.threads[0]; firstThread.samples = firstThread.samples.filter(e => { return e.time >= profilingStartTime; }); } /** * Offsets all the samples in the provided profiler data by the specified time. * * @param object profilerData * The profiler data received from the backend. * @param number timeOffset * The amount of time to offset by (in milliseconds). */ function offsetSampleTimes(profilerData, timeOffset) { let firstThreadSamples = profilerData.profile.threads[0].samples; for (let sample of firstThreadSamples) { sample.time -= timeOffset; } } /** * A collection of small wrappers promisifying functions invoking callbacks. */ Loading browser/devtools/performance/modules/io.js +5 −10 Original line number Diff line number Diff line Loading @@ -130,21 +130,16 @@ function isValidSerializerVersion (version) { function convertLegacyData (legacyData) { let { profilerData, ticksData, recordingDuration } = legacyData; // The `profilerData` stays, and the previously unrecorded fields // just are empty arrays. // The `profilerData` and `ticksData` stay, but the previously unrecorded // fields just are empty arrays. let data = { label: profilerData.profilerLabel, duration: recordingDuration, markers: [], frames: [], memory: [], ticks: ticksData, profilerData: profilerData, // Data from the original profiler won't contain `interval` fields, // but a recording duration, as well as the current time, which can be used // to infer the interval startTime and endTime. interval: { startTime: profilerData.currentTime - recordingDuration, endTime: profilerData.currentTime } profile: profilerData.profile }; return data; Loading browser/devtools/performance/modules/recording-model.js +82 −85 Original line number Diff line number Diff line Loading @@ -3,13 +3,17 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const { PerformanceIO } = require("devtools/performance/io"); const { Cc, Ci, Cu, Cr } = require("chrome"); loader.lazyRequireGetter(this, "PerformanceIO", "devtools/performance/io", true); loader.lazyRequireGetter(this, "RecordingUtils", "devtools/performance/recording-utils", true); const RECORDING_IN_PROGRESS = exports.RECORDING_IN_PROGRESS = -1; const RECORDING_UNAVAILABLE = exports.RECORDING_UNAVAILABLE = null; /** * Model for a wholistic profile, containing start/stop times, profiling data, frames data, * timeline (marker, tick, memory) data, and methods to start/stop recording. * Model for a wholistic profile, containing the duration, profiling data, * frames data, timeline (marker, tick, memory) data, and methods to mark * a recording as 'in progress' or 'finished'. */ const RecordingModel = function (options={}) { Loading @@ -19,17 +23,20 @@ const RecordingModel = function (options={}) { }; RecordingModel.prototype = { _localStartTime: RECORDING_UNAVAILABLE, _startTime: RECORDING_UNAVAILABLE, _endTime: RECORDING_UNAVAILABLE, _markers: [], _frames: [], _ticks: [], _memory: [], _profilerData: {}, _label: "", // Private fields, only needed when a recording is started or stopped. _imported: false, _isRecording: false, _recording: false, _profilerStartTime: 0, _timelineStartTime: 0, // Serializable fields, necessary and sufficient for import and export. _label: "", _duration: 0, _markers: null, _frames: null, _ticks: null, _memory: null, _profile: null, /** * Loads a recording from a file. Loading @@ -41,16 +48,13 @@ RecordingModel.prototype = { let recordingData = yield PerformanceIO.loadRecordingFromFile(file); this._imported = true; this._label = recordingData.profilerData.profilerLabel || ""; this._startTime = recordingData.interval.startTime; this._endTime = recordingData.interval.endTime; this._label = recordingData.label || ""; this._duration = recordingData.duration; this._markers = recordingData.markers; this._frames = recordingData.frames; this._memory = recordingData.memory; this._ticks = recordingData.ticks; this._profilerData = recordingData.profilerData; return recordingData; this._profile = recordingData.profile; }), /** Loading @@ -65,24 +69,24 @@ RecordingModel.prototype = { }), /** * Starts recording with the PerformanceFront, storing the start times * on the model. * Starts recording with the PerformanceFront. * * @param object options * An options object to pass to the timeline front. Supported * properties are `withTicks` and `withMemory`. */ startRecording: Task.async(function *() { startRecording: Task.async(function *(options = {}) { // Times must come from the actor in order to be self-consistent. // However, we also want to update the view with the elapsed time // even when the actor is not generating data. To do this we get // the local time and use it to compute a reasonable elapsed time. this._localStartTime = this._performance.now(); let { startTime } = yield this._front.startRecording({ withTicks: true, withMemory: true }); this._isRecording = true; let info = yield this._front.startRecording(options); this._profilerStartTime = info.profilerStartTime; this._timelineStartTime = info.timelineStartTime; this._recording = true; this._startTime = startTime; this._endTime = RECORDING_IN_PROGRESS; this._markers = []; this._frames = []; this._memory = []; Loading @@ -90,63 +94,46 @@ RecordingModel.prototype = { }), /** * Stops recording with the PerformanceFront, storing the end times * on the model. * Stops recording with the PerformanceFront. */ stopRecording: Task.async(function *() { let results = yield this._front.stopRecording(); this._isRecording = false; // If `endTime` is not yielded from timeline actor (< Fx36), fake it. if (!results.endTime) { results.endTime = this._startTime + this.getLocalElapsedTime(); } this._endTime = results.endTime; this._profilerData = results.profilerData; let info = yield this._front.stopRecording(); this._profile = info.profile; this._duration = info.profilerEndTime - this._profilerStartTime; this._recording = false; // We'll need to filter out all samples that fall out of current profile's // range since the profiler is continuously running. Because of this, sample // times are not guaranteed to have a zero epoch, so offset the timestamps. RecordingUtils.filterSamples(this._profile, this._profilerStartTime); RecordingUtils.offsetSampleTimes(this._profile, this._profilerStartTime); // Markers need to be sorted ascending by time, to be properly displayed // in a waterfall view. this._markers = this._markers.sort((a, b) => (a.start > b.start)); return results; }), /** * Returns the profile's label, from `console.profile(LABEL)`. * Gets the profile's label, from `console.profile(LABEL)`. * @return string */ getLabel: function () { return this._label; }, /** * Gets the amount of time elapsed locally after starting a recording. */ getLocalElapsedTime: function () { return this._performance.now() - this._localStartTime; }, /** * Returns duration of this recording, in milliseconds. * Gets duration of this recording, in milliseconds. * @return number */ getDuration: function () { let { startTime, endTime } = this.getInterval(); return endTime - startTime; }, /** * Gets the time interval for the current recording. * @return object */ getInterval: function() { let startTime = this._startTime; let endTime = this._endTime; // Compute an approximate ending time for the current recording. This is // needed to ensure that the view updates even when new data is // not being generated. if (endTime == RECORDING_IN_PROGRESS) { endTime = startTime + this.getLocalElapsedTime(); // Compute an approximate ending time for the current recording if it is // still in progress. This is needed to ensure that the view updates even // when new data is not being generated. if (this._recording) { return this._performance.now() - this._localStartTime; } else { return this._duration; } return { startTime, endTime }; }, /** Loading Loading @@ -185,21 +172,22 @@ RecordingModel.prototype = { * Gets the profiler data in this recording. * @return array */ getProfilerData: function() { return this._profilerData; getProfile: function() { return this._profile; }, /** * Gets all the data in this recording. */ getAllData: function() { let interval = this.getInterval(); let label = this.getLabel(); let duration = this.getDuration(); let markers = this.getMarkers(); let frames = this.getFrames(); let memory = this.getMemory(); let ticks = this.getTicks(); let profilerData = this.getProfilerData(); return { interval, markers, frames, memory, ticks, profilerData }; let profile = this.getProfile(); return { label, duration, markers, frames, memory, ticks, profile }; }, /** Loading @@ -207,7 +195,7 @@ RecordingModel.prototype = { * is recording. */ isRecording: function () { return this._isRecording; return this._recording; }, /** Loading @@ -216,26 +204,35 @@ RecordingModel.prototype = { addTimelineData: function (eventName, ...data) { // If this model isn't currently recording, // ignore the timeline data. if (!this.isRecording()) { if (!this._recording) { return; } switch (eventName) { // Accumulate markers into an array. // Accumulate markers into an array. Furthermore, timestamps do not // have a zero epoch, so offset all of them by the timeline's start time. case "markers": let [markers] = data; RecordingUtils.offsetMarkerTimes(markers, this._timelineStartTime); Array.prototype.push.apply(this._markers, markers); break; // Accumulate stack frames into an array. case "frames": let [, frames] = data; Array.prototype.push.apply(this._frames, frames); break; // Accumulate memory measurements into an array. // Accumulate memory measurements into an array. Furthermore, the // timestamp does not have a zero epoch, so offset it. case "memory": let [delta, measurement] = data; this._memory.push({ delta, value: measurement.total / 1024 / 1024 }); let [currentTime, measurement] = data; this._memory.push({ delta: currentTime - this._timelineStartTime, value: measurement.total / 1024 / 1024 }); break; // Save the accumulated refresh driver ticks. case "ticks": let [, timestamps] = data; Loading browser/devtools/performance/modules/recording-utils.js 0 → 100644 +59 −0 Original line number Diff line number Diff line /* 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"; /** * Utility functions for managing recording models and their internal data, * such as filtering profile samples or offsetting timestamps. */ exports.RecordingUtils = {}; /** * Filters all the samples in the provided profiler data to be more recent * than the specified start time. * * @param object profile * The profiler data received from the backend. * @param number profilerStartTime * The earliest acceptable sample time (in milliseconds). */ exports.RecordingUtils.filterSamples = function(profile, profilerStartTime) { let firstThread = profile.threads[0]; firstThread.samples = firstThread.samples.filter(e => { return e.time >= profilerStartTime; }); } /** * Offsets all the samples in the provided profiler data by the specified time. * * @param object profile * The profiler data received from the backend. * @param number timeOffset * The amount of time to offset by (in milliseconds). */ exports.RecordingUtils.offsetSampleTimes = function(profile, timeOffset) { let firstThread = profile.threads[0]; for (let sample of firstThread.samples) { sample.time -= timeOffset; } } /** * Offsets all the markers in the provided timeline data by the specified time. * * @param array markers * The markers array received from the backend. * @param number timeOffset * The amount of time to offset by (in milliseconds). */ exports.RecordingUtils.offsetMarkerTimes = function(markers, timeOffset) { for (let marker of markers) { marker.start -= timeOffset; marker.end -= timeOffset; } } browser/devtools/performance/moz.build +1 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ EXTRA_JS_MODULES.devtools.performance += [ 'modules/front.js', 'modules/io.js', 'modules/recording-model.js', 'modules/recording-utils.js', 'panel.js' ] Loading Loading
browser/devtools/performance/modules/front.js +30 −59 Original line number Diff line number Diff line Loading @@ -31,6 +31,8 @@ let SharedPerformanceActors = new WeakMap(); * * @param Target target * The target owning this connection. * @return PerformanceActorsConnection * The shared connection for the specified target. */ SharedPerformanceActors.forTarget = function(target) { if (this.has(target)) { Loading Loading @@ -140,12 +142,13 @@ PerformanceActorsConnection.prototype = { this._timeline = new TimelineFront(this._target.client, this._target.form); } else { this._timeline = { start: () => {}, stop: () => {}, start: () => 0, stop: () => 0, isRecording: () => false, on: () => {}, off: () => {}, destroy: () => {} on: () => null, off: () => null, once: () => promise.reject(), destroy: () => null }; } }, Loading Loading @@ -211,36 +214,41 @@ PerformanceFront.prototype = { /** * Manually begins a recording session. * * @param object options * @param object timelineOptions * An options object to pass to the timeline front. Supported * properties are `withTicks` and `withMemory`. * @return object * A promise that is resolved once recording has started. */ startRecording: Task.async(function*(options = {}) { let { isActive, currentTime } = yield this._request("profiler", "isActive"); startRecording: Task.async(function*(timelineOptions = {}) { let profilerStatus = yield this._request("profiler", "isActive"); let profilerStartTime; // Start the profiler only if it wasn't already active. The built-in // nsIPerformance module will be kept recording, because it's the same instance // for all targets and interacts with the whole platform, so we don't want // to affect other clients by stopping (or restarting) it. if (!isActive) { if (!profilerStatus.isActive) { // Extend the profiler options so that protocol.js doesn't modify the original. let profilerOptions = extend({}, this._customProfilerOptions); yield this._request("profiler", "startProfiler", profilerOptions); this._profilingStartTime = 0; profilerStartTime = 0; this.emit("profiler-activated"); } else { this._profilingStartTime = currentTime; profilerStartTime = profilerStatus.currentTime; this.emit("profiler-already-active"); } // The timeline actor is target-dependent, so just make sure // it's recording. let startTime = yield this._request("timeline", "start", options); // The timeline actor is target-dependent, so just make sure it's recording. // It won't, however, be available in older Geckos (FF < 35). let timelineStartTime = yield this._request("timeline", "start", timelineOptions); // Return only the start time from the timeline actor. return { startTime }; // Return the start times from the two actors. They will be used to // synchronize the profiler and timeline data. return { profilerStartTime, timelineStartTime }; }), /** Loading @@ -251,19 +259,15 @@ PerformanceFront.prototype = { * with the profiler and timeline data. */ stopRecording: Task.async(function*() { // We'll need to filter out all samples that fall out of current profile's // range. This is necessary because the profiler is continuously running. let timelineEndTime = yield this._request("timeline", "stop"); let profilerData = yield this._request("profiler", "getProfile"); filterSamples(profilerData, this._profilingStartTime); offsetSampleTimes(profilerData, this._profilingStartTime); let endTime = yield this._request("timeline", "stop"); // Join all the acquired data and return it for outside consumers. // Return the end times from the two actors. They will be used to // synchronize the profiler and timeline data. return { recordingDuration: profilerData.currentTime - this._profilingStartTime, profilerData: profilerData, endTime: endTime profile: profilerData.profile, profilerEndTime: profilerData.currentTime, timelineEndTime: timelineEndTime }; }), Loading @@ -280,39 +284,6 @@ PerformanceFront.prototype = { } }; /** * Filters all the samples in the provided profiler data to be more recent * than the specified start time. * * @param object profilerData * The profiler data received from the backend. * @param number profilingStartTime * The earliest acceptable sample time (in milliseconds). */ function filterSamples(profilerData, profilingStartTime) { let firstThread = profilerData.profile.threads[0]; firstThread.samples = firstThread.samples.filter(e => { return e.time >= profilingStartTime; }); } /** * Offsets all the samples in the provided profiler data by the specified time. * * @param object profilerData * The profiler data received from the backend. * @param number timeOffset * The amount of time to offset by (in milliseconds). */ function offsetSampleTimes(profilerData, timeOffset) { let firstThreadSamples = profilerData.profile.threads[0].samples; for (let sample of firstThreadSamples) { sample.time -= timeOffset; } } /** * A collection of small wrappers promisifying functions invoking callbacks. */ Loading
browser/devtools/performance/modules/io.js +5 −10 Original line number Diff line number Diff line Loading @@ -130,21 +130,16 @@ function isValidSerializerVersion (version) { function convertLegacyData (legacyData) { let { profilerData, ticksData, recordingDuration } = legacyData; // The `profilerData` stays, and the previously unrecorded fields // just are empty arrays. // The `profilerData` and `ticksData` stay, but the previously unrecorded // fields just are empty arrays. let data = { label: profilerData.profilerLabel, duration: recordingDuration, markers: [], frames: [], memory: [], ticks: ticksData, profilerData: profilerData, // Data from the original profiler won't contain `interval` fields, // but a recording duration, as well as the current time, which can be used // to infer the interval startTime and endTime. interval: { startTime: profilerData.currentTime - recordingDuration, endTime: profilerData.currentTime } profile: profilerData.profile }; return data; Loading
browser/devtools/performance/modules/recording-model.js +82 −85 Original line number Diff line number Diff line Loading @@ -3,13 +3,17 @@ * You can obtain one at http://mozilla.org/MPL/2.0/. */ "use strict"; const { PerformanceIO } = require("devtools/performance/io"); const { Cc, Ci, Cu, Cr } = require("chrome"); loader.lazyRequireGetter(this, "PerformanceIO", "devtools/performance/io", true); loader.lazyRequireGetter(this, "RecordingUtils", "devtools/performance/recording-utils", true); const RECORDING_IN_PROGRESS = exports.RECORDING_IN_PROGRESS = -1; const RECORDING_UNAVAILABLE = exports.RECORDING_UNAVAILABLE = null; /** * Model for a wholistic profile, containing start/stop times, profiling data, frames data, * timeline (marker, tick, memory) data, and methods to start/stop recording. * Model for a wholistic profile, containing the duration, profiling data, * frames data, timeline (marker, tick, memory) data, and methods to mark * a recording as 'in progress' or 'finished'. */ const RecordingModel = function (options={}) { Loading @@ -19,17 +23,20 @@ const RecordingModel = function (options={}) { }; RecordingModel.prototype = { _localStartTime: RECORDING_UNAVAILABLE, _startTime: RECORDING_UNAVAILABLE, _endTime: RECORDING_UNAVAILABLE, _markers: [], _frames: [], _ticks: [], _memory: [], _profilerData: {}, _label: "", // Private fields, only needed when a recording is started or stopped. _imported: false, _isRecording: false, _recording: false, _profilerStartTime: 0, _timelineStartTime: 0, // Serializable fields, necessary and sufficient for import and export. _label: "", _duration: 0, _markers: null, _frames: null, _ticks: null, _memory: null, _profile: null, /** * Loads a recording from a file. Loading @@ -41,16 +48,13 @@ RecordingModel.prototype = { let recordingData = yield PerformanceIO.loadRecordingFromFile(file); this._imported = true; this._label = recordingData.profilerData.profilerLabel || ""; this._startTime = recordingData.interval.startTime; this._endTime = recordingData.interval.endTime; this._label = recordingData.label || ""; this._duration = recordingData.duration; this._markers = recordingData.markers; this._frames = recordingData.frames; this._memory = recordingData.memory; this._ticks = recordingData.ticks; this._profilerData = recordingData.profilerData; return recordingData; this._profile = recordingData.profile; }), /** Loading @@ -65,24 +69,24 @@ RecordingModel.prototype = { }), /** * Starts recording with the PerformanceFront, storing the start times * on the model. * Starts recording with the PerformanceFront. * * @param object options * An options object to pass to the timeline front. Supported * properties are `withTicks` and `withMemory`. */ startRecording: Task.async(function *() { startRecording: Task.async(function *(options = {}) { // Times must come from the actor in order to be self-consistent. // However, we also want to update the view with the elapsed time // even when the actor is not generating data. To do this we get // the local time and use it to compute a reasonable elapsed time. this._localStartTime = this._performance.now(); let { startTime } = yield this._front.startRecording({ withTicks: true, withMemory: true }); this._isRecording = true; let info = yield this._front.startRecording(options); this._profilerStartTime = info.profilerStartTime; this._timelineStartTime = info.timelineStartTime; this._recording = true; this._startTime = startTime; this._endTime = RECORDING_IN_PROGRESS; this._markers = []; this._frames = []; this._memory = []; Loading @@ -90,63 +94,46 @@ RecordingModel.prototype = { }), /** * Stops recording with the PerformanceFront, storing the end times * on the model. * Stops recording with the PerformanceFront. */ stopRecording: Task.async(function *() { let results = yield this._front.stopRecording(); this._isRecording = false; // If `endTime` is not yielded from timeline actor (< Fx36), fake it. if (!results.endTime) { results.endTime = this._startTime + this.getLocalElapsedTime(); } this._endTime = results.endTime; this._profilerData = results.profilerData; let info = yield this._front.stopRecording(); this._profile = info.profile; this._duration = info.profilerEndTime - this._profilerStartTime; this._recording = false; // We'll need to filter out all samples that fall out of current profile's // range since the profiler is continuously running. Because of this, sample // times are not guaranteed to have a zero epoch, so offset the timestamps. RecordingUtils.filterSamples(this._profile, this._profilerStartTime); RecordingUtils.offsetSampleTimes(this._profile, this._profilerStartTime); // Markers need to be sorted ascending by time, to be properly displayed // in a waterfall view. this._markers = this._markers.sort((a, b) => (a.start > b.start)); return results; }), /** * Returns the profile's label, from `console.profile(LABEL)`. * Gets the profile's label, from `console.profile(LABEL)`. * @return string */ getLabel: function () { return this._label; }, /** * Gets the amount of time elapsed locally after starting a recording. */ getLocalElapsedTime: function () { return this._performance.now() - this._localStartTime; }, /** * Returns duration of this recording, in milliseconds. * Gets duration of this recording, in milliseconds. * @return number */ getDuration: function () { let { startTime, endTime } = this.getInterval(); return endTime - startTime; }, /** * Gets the time interval for the current recording. * @return object */ getInterval: function() { let startTime = this._startTime; let endTime = this._endTime; // Compute an approximate ending time for the current recording. This is // needed to ensure that the view updates even when new data is // not being generated. if (endTime == RECORDING_IN_PROGRESS) { endTime = startTime + this.getLocalElapsedTime(); // Compute an approximate ending time for the current recording if it is // still in progress. This is needed to ensure that the view updates even // when new data is not being generated. if (this._recording) { return this._performance.now() - this._localStartTime; } else { return this._duration; } return { startTime, endTime }; }, /** Loading Loading @@ -185,21 +172,22 @@ RecordingModel.prototype = { * Gets the profiler data in this recording. * @return array */ getProfilerData: function() { return this._profilerData; getProfile: function() { return this._profile; }, /** * Gets all the data in this recording. */ getAllData: function() { let interval = this.getInterval(); let label = this.getLabel(); let duration = this.getDuration(); let markers = this.getMarkers(); let frames = this.getFrames(); let memory = this.getMemory(); let ticks = this.getTicks(); let profilerData = this.getProfilerData(); return { interval, markers, frames, memory, ticks, profilerData }; let profile = this.getProfile(); return { label, duration, markers, frames, memory, ticks, profile }; }, /** Loading @@ -207,7 +195,7 @@ RecordingModel.prototype = { * is recording. */ isRecording: function () { return this._isRecording; return this._recording; }, /** Loading @@ -216,26 +204,35 @@ RecordingModel.prototype = { addTimelineData: function (eventName, ...data) { // If this model isn't currently recording, // ignore the timeline data. if (!this.isRecording()) { if (!this._recording) { return; } switch (eventName) { // Accumulate markers into an array. // Accumulate markers into an array. Furthermore, timestamps do not // have a zero epoch, so offset all of them by the timeline's start time. case "markers": let [markers] = data; RecordingUtils.offsetMarkerTimes(markers, this._timelineStartTime); Array.prototype.push.apply(this._markers, markers); break; // Accumulate stack frames into an array. case "frames": let [, frames] = data; Array.prototype.push.apply(this._frames, frames); break; // Accumulate memory measurements into an array. // Accumulate memory measurements into an array. Furthermore, the // timestamp does not have a zero epoch, so offset it. case "memory": let [delta, measurement] = data; this._memory.push({ delta, value: measurement.total / 1024 / 1024 }); let [currentTime, measurement] = data; this._memory.push({ delta: currentTime - this._timelineStartTime, value: measurement.total / 1024 / 1024 }); break; // Save the accumulated refresh driver ticks. case "ticks": let [, timestamps] = data; Loading
browser/devtools/performance/modules/recording-utils.js 0 → 100644 +59 −0 Original line number Diff line number Diff line /* 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"; /** * Utility functions for managing recording models and their internal data, * such as filtering profile samples or offsetting timestamps. */ exports.RecordingUtils = {}; /** * Filters all the samples in the provided profiler data to be more recent * than the specified start time. * * @param object profile * The profiler data received from the backend. * @param number profilerStartTime * The earliest acceptable sample time (in milliseconds). */ exports.RecordingUtils.filterSamples = function(profile, profilerStartTime) { let firstThread = profile.threads[0]; firstThread.samples = firstThread.samples.filter(e => { return e.time >= profilerStartTime; }); } /** * Offsets all the samples in the provided profiler data by the specified time. * * @param object profile * The profiler data received from the backend. * @param number timeOffset * The amount of time to offset by (in milliseconds). */ exports.RecordingUtils.offsetSampleTimes = function(profile, timeOffset) { let firstThread = profile.threads[0]; for (let sample of firstThread.samples) { sample.time -= timeOffset; } } /** * Offsets all the markers in the provided timeline data by the specified time. * * @param array markers * The markers array received from the backend. * @param number timeOffset * The amount of time to offset by (in milliseconds). */ exports.RecordingUtils.offsetMarkerTimes = function(markers, timeOffset) { for (let marker of markers) { marker.start -= timeOffset; marker.end -= timeOffset; } }
browser/devtools/performance/moz.build +1 −0 Original line number Diff line number Diff line Loading @@ -7,6 +7,7 @@ EXTRA_JS_MODULES.devtools.performance += [ 'modules/front.js', 'modules/io.js', 'modules/recording-model.js', 'modules/recording-utils.js', 'panel.js' ] Loading