зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1077461 - Build call tree for memory allocations view, r=jsantell
--HG-- rename : browser/devtools/performance/test/browser_perf-options-invert-call-tree.js => browser/devtools/performance/test/browser_perf-options-invert-call-tree-01.js rename : browser/devtools/performance/views/details-call-tree.js => browser/devtools/performance/views/details-js-call-tree.js rename : browser/devtools/performance/views/details-flamegraph.js => browser/devtools/performance/views/details-js-flamegraph.js
This commit is contained in:
Родитель
4e7fdc601c
Коммит
8799c47957
|
@ -99,9 +99,11 @@ browser.jar:
|
|||
content/browser/devtools/performance/views/toolbar.js (performance/views/toolbar.js)
|
||||
content/browser/devtools/performance/views/details.js (performance/views/details.js)
|
||||
content/browser/devtools/performance/views/details-subview.js (performance/views/details-abstract-subview.js)
|
||||
content/browser/devtools/performance/views/details-call-tree.js (performance/views/details-call-tree.js)
|
||||
content/browser/devtools/performance/views/details-waterfall.js (performance/views/details-waterfall.js)
|
||||
content/browser/devtools/performance/views/details-flamegraph.js (performance/views/details-flamegraph.js)
|
||||
content/browser/devtools/performance/views/details-js-call-tree.js (performance/views/details-js-call-tree.js)
|
||||
content/browser/devtools/performance/views/details-js-flamegraph.js (performance/views/details-js-flamegraph.js)
|
||||
content/browser/devtools/performance/views/details-memory-call-tree.js (performance/views/details-memory-call-tree.js)
|
||||
content/browser/devtools/performance/views/details-memory-flamegraph.js (performance/views/details-memory-flamegraph.js)
|
||||
content/browser/devtools/performance/views/recordings.js (performance/views/recordings.js)
|
||||
#endif
|
||||
content/browser/devtools/responsivedesign/resize-commands.js (responsivedesign/resize-commands.js)
|
||||
|
|
|
@ -13,12 +13,23 @@ loader.lazyRequireGetter(this, "EventEmitter",
|
|||
"devtools/toolkit/event-emitter");
|
||||
loader.lazyRequireGetter(this, "TimelineFront",
|
||||
"devtools/server/actors/timeline", true);
|
||||
loader.lazyRequireGetter(this, "MemoryFront",
|
||||
"devtools/server/actors/memory", true);
|
||||
|
||||
loader.lazyRequireGetter(this, "DevToolsUtils",
|
||||
"devtools/toolkit/DevToolsUtils");
|
||||
|
||||
loader.lazyImporter(this, "gDevTools",
|
||||
"resource:///modules/devtools/gDevTools.jsm");
|
||||
|
||||
loader.lazyImporter(this, "setTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
loader.lazyImporter(this, "clearTimeout",
|
||||
"resource://gre/modules/Timer.jsm");
|
||||
|
||||
// How often do we pull allocation sites from the memory actor.
|
||||
const DEFAULT_ALLOCATION_SITES_PULL_TIMEOUT = 200; // ms
|
||||
|
||||
/**
|
||||
* A cache of all PerformanceActorsConnection instances.
|
||||
* The keys are Target objects.
|
||||
|
@ -44,6 +55,25 @@ SharedPerformanceActors.forTarget = function(target) {
|
|||
return instance;
|
||||
};
|
||||
|
||||
/**
|
||||
* A dummy front decorated with the provided methods.
|
||||
*
|
||||
* @param array blueprint
|
||||
* A list of [funcName, retVal] describing the class.
|
||||
*/
|
||||
function MockedFront(blueprint) {
|
||||
EventEmitter.decorate(this);
|
||||
|
||||
for (let [funcName, retVal] of blueprint) {
|
||||
this[funcName] = (x => x).bind(this, retVal);
|
||||
}
|
||||
}
|
||||
|
||||
MockedFront.prototype = {
|
||||
initialize: function() {},
|
||||
destroy: function() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* A connection to underlying actors (profiler, memory, framerate, etc.)
|
||||
* shared by all tools in a target.
|
||||
|
@ -67,7 +97,7 @@ function PerformanceActorsConnection(target) {
|
|||
PerformanceActorsConnection.prototype = {
|
||||
/**
|
||||
* Initializes a connection to the profiler and other miscellaneous actors.
|
||||
* If already open, nothing happens.
|
||||
* If in the process of opening, or already open, nothing happens.
|
||||
*
|
||||
* @return object
|
||||
* A promise that is resolved once the connection is established.
|
||||
|
@ -80,11 +110,13 @@ PerformanceActorsConnection.prototype = {
|
|||
// Local debugging needs to make the target remote.
|
||||
yield this._target.makeRemote();
|
||||
|
||||
// Sets `this._profiler`
|
||||
// Sets `this._profiler`, `this._timeline` and `this._memory`.
|
||||
// Only initialize the timeline and memory fronts if the respective actors
|
||||
// are available. Older Gecko versions don't have existing implementations,
|
||||
// in which case all the methods we need can be easily mocked.
|
||||
yield this._connectProfilerActor();
|
||||
|
||||
// Sets or shims `this._timeline`
|
||||
yield this._connectTimelineActor();
|
||||
yield this._connectMemoryActor();
|
||||
|
||||
this._connected = true;
|
||||
|
||||
|
@ -94,10 +126,10 @@ PerformanceActorsConnection.prototype = {
|
|||
/**
|
||||
* Destroys this connection.
|
||||
*/
|
||||
destroy: function () {
|
||||
this._disconnectActors();
|
||||
destroy: Task.async(function*() {
|
||||
yield this._disconnectActors();
|
||||
this._connected = false;
|
||||
},
|
||||
}),
|
||||
|
||||
/**
|
||||
* Initializes a connection to the profiler actor.
|
||||
|
@ -126,46 +158,51 @@ PerformanceActorsConnection.prototype = {
|
|||
|
||||
/**
|
||||
* Initializes a connection to a timeline actor.
|
||||
* TODO: use framework level feature detection from bug 1069673
|
||||
*/
|
||||
_connectTimelineActor: function() {
|
||||
// Only initialize the timeline front if the respective actor is available.
|
||||
// Older Gecko versions don't have an existing implementation, in which case
|
||||
// all the methods we need can be easily mocked.
|
||||
//
|
||||
// If the timeline actor exists, all underlying actors (memory, framerate) exist,
|
||||
// with the expected methods and behaviour. If using the Performance tool,
|
||||
// and timeline actor does not exist (FxOS devices < Gecko 35),
|
||||
// then just use the mocked actor and do not display timeline data.
|
||||
//
|
||||
// TODO use framework level feature detection from bug 1069673
|
||||
if (this._target.form && this._target.form.timelineActor) {
|
||||
this._timeline = new TimelineFront(this._target.client, this._target.form);
|
||||
} else {
|
||||
this._timeline = {
|
||||
start: () => 0,
|
||||
stop: () => 0,
|
||||
isRecording: () => false,
|
||||
on: () => null,
|
||||
off: () => null,
|
||||
once: () => promise.reject(),
|
||||
destroy: () => null
|
||||
};
|
||||
this._timeline = new MockedFront([
|
||||
["start", 0],
|
||||
["stop", 0]
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Initializes a connection to a memory actor.
|
||||
* TODO: use framework level feature detection from bug 1069673
|
||||
*/
|
||||
_connectMemoryActor: function() {
|
||||
if (this._target.form && this._target.form.memoryActor) {
|
||||
this._memory = new MemoryFront(this._target.client, this._target.form);
|
||||
} else {
|
||||
this._memory = new MockedFront([
|
||||
["attach"],
|
||||
["detach"],
|
||||
["startRecordingAllocations", 0],
|
||||
["stopRecordingAllocations", 0],
|
||||
["getAllocations"]
|
||||
]);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Closes the connections to non-profiler actors.
|
||||
*/
|
||||
_disconnectActors: function () {
|
||||
this._timeline.destroy();
|
||||
},
|
||||
_disconnectActors: Task.async(function* () {
|
||||
yield this._timeline.destroy();
|
||||
yield this._memory.destroy();
|
||||
}),
|
||||
|
||||
/**
|
||||
* Sends the request over the remote debugging protocol to the
|
||||
* specified actor.
|
||||
*
|
||||
* @param string actor
|
||||
* The designated actor. Currently supported: "profiler", "timeline".
|
||||
* Currently supported: "profiler", "timeline", "memory".
|
||||
* @param string method
|
||||
* Method to call on the backend.
|
||||
* @param any args [optional]
|
||||
|
@ -188,6 +225,11 @@ PerformanceActorsConnection.prototype = {
|
|||
if (actor == "timeline") {
|
||||
return this._timeline[method].apply(this._timeline, args);
|
||||
}
|
||||
|
||||
// Handle requests to the memory actor.
|
||||
if (actor == "memory") {
|
||||
return this._memory[method].apply(this._memory, args);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -208,69 +250,142 @@ function PerformanceFront(connection) {
|
|||
connection._timeline.on("frames", (delta, frames) => this.emit("frames", delta, frames));
|
||||
connection._timeline.on("memory", (delta, measurement) => this.emit("memory", delta, measurement));
|
||||
connection._timeline.on("ticks", (delta, timestamps) => this.emit("ticks", delta, timestamps));
|
||||
|
||||
this._pullAllocationSites = this._pullAllocationSites.bind(this);
|
||||
this._sitesPullTimeout = 0;
|
||||
}
|
||||
|
||||
PerformanceFront.prototype = {
|
||||
/**
|
||||
* Manually begins a recording session.
|
||||
*
|
||||
* @param object timelineOptions
|
||||
* An options object to pass to the timeline front. Supported
|
||||
* properties are `withTicks` and `withMemory`.
|
||||
* @param object options
|
||||
* An options object to pass to the actors. Supported properties are
|
||||
* `withTicks`, `withMemory` and `withAllocations`.
|
||||
* @return object
|
||||
* A promise that is resolved once recording has started.
|
||||
*/
|
||||
startRecording: Task.async(function*(timelineOptions = {}) {
|
||||
let profilerStatus = yield this._request("profiler", "isActive");
|
||||
let profilerStartTime;
|
||||
startRecording: Task.async(function*(options = {}) {
|
||||
// All actors are started asynchronously over the remote debugging protocol.
|
||||
// Get the corresponding start times from each one of them.
|
||||
let profilerStartTime = yield this._startProfiler();
|
||||
let timelineStartTime = yield this._startTimeline(options);
|
||||
let memoryStartTime = yield this._startMemory(options);
|
||||
|
||||
// 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 (!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);
|
||||
profilerStartTime = 0;
|
||||
this.emit("profiler-activated");
|
||||
} else {
|
||||
profilerStartTime = profilerStatus.currentTime;
|
||||
this.emit("profiler-already-active");
|
||||
}
|
||||
|
||||
// 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 the start times from the two actors. They will be used to
|
||||
// synchronize the profiler and timeline data.
|
||||
return {
|
||||
profilerStartTime,
|
||||
timelineStartTime
|
||||
timelineStartTime,
|
||||
memoryStartTime
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* Manually ends the current recording session.
|
||||
*
|
||||
* @param object options
|
||||
* @see PerformanceFront.prototype.startRecording
|
||||
* @return object
|
||||
* A promise that is resolved once recording has stopped,
|
||||
* with the profiler and timeline data.
|
||||
* with the profiler and memory data, along with all the end times.
|
||||
*/
|
||||
stopRecording: Task.async(function*() {
|
||||
let timelineEndTime = yield this._request("timeline", "stop");
|
||||
stopRecording: Task.async(function*(options = {}) {
|
||||
let memoryEndTime = yield this._stopMemory(options);
|
||||
let timelineEndTime = yield this._stopTimeline(options);
|
||||
let profilerData = yield this._request("profiler", "getProfile");
|
||||
|
||||
// Return the end times from the two actors. They will be used to
|
||||
// synchronize the profiler and timeline data.
|
||||
return {
|
||||
// Data available only at the end of a recording.
|
||||
profile: profilerData.profile,
|
||||
|
||||
// End times for all the actors.
|
||||
profilerEndTime: profilerData.currentTime,
|
||||
timelineEndTime: timelineEndTime
|
||||
timelineEndTime: timelineEndTime,
|
||||
memoryEndTime: memoryEndTime
|
||||
};
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts the profiler actor, if necessary.
|
||||
*/
|
||||
_startProfiler: Task.async(function *() {
|
||||
// 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.
|
||||
let profilerStatus = yield this._request("profiler", "isActive");
|
||||
if (profilerStatus.isActive) {
|
||||
this.emit("profiler-already-active");
|
||||
return profilerStatus.currentTime;
|
||||
}
|
||||
|
||||
// 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.emit("profiler-activated");
|
||||
return 0;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts the timeline actor.
|
||||
*/
|
||||
_startTimeline: Task.async(function *(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).
|
||||
return (yield this._request("timeline", "start", options));
|
||||
}),
|
||||
|
||||
/**
|
||||
* Stops the timeline actor.
|
||||
*/
|
||||
_stopTimeline: Task.async(function *(options) {
|
||||
return (yield this._request("timeline", "stop"));
|
||||
}),
|
||||
|
||||
/**
|
||||
* Starts the timeline actor, if necessary.
|
||||
*/
|
||||
_startMemory: Task.async(function *(options) {
|
||||
if (!options.withAllocations) {
|
||||
return 0;
|
||||
}
|
||||
yield this._request("memory", "attach");
|
||||
let memoryStartTime = yield this._request("memory", "startRecordingAllocations");
|
||||
yield this._pullAllocationSites();
|
||||
return memoryStartTime;
|
||||
}),
|
||||
|
||||
/**
|
||||
* Stops the timeline actor, if necessary.
|
||||
*/
|
||||
_stopMemory: Task.async(function *(options) {
|
||||
if (!options.withAllocations) {
|
||||
return 0;
|
||||
}
|
||||
clearTimeout(this._sitesPullTimeout);
|
||||
let memoryEndTime = yield this._request("memory", "stopRecordingAllocations");
|
||||
yield this._request("memory", "detach");
|
||||
return memoryEndTime;
|
||||
}),
|
||||
|
||||
/**
|
||||
* At regular intervals, pull allocations from the memory actor, and forward
|
||||
* them to consumers.
|
||||
*/
|
||||
_pullAllocationSites: Task.async(function *() {
|
||||
let memoryData = yield this._request("memory", "getAllocations");
|
||||
|
||||
this.emit("allocations", {
|
||||
sites: memoryData.allocations,
|
||||
timestamps: memoryData.allocationsTimestamps,
|
||||
frames: memoryData.frames,
|
||||
counts: memoryData.counts
|
||||
});
|
||||
|
||||
let delay = DEFAULT_ALLOCATION_SITES_PULL_TIMEOUT;
|
||||
this._sitesPullTimeout = setTimeout(this._pullAllocationSites, delay);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Overrides the options sent to the built-in profiler module when activating,
|
||||
* such as the maximum entries count, the sampling interval etc.
|
||||
|
@ -285,7 +400,8 @@ PerformanceFront.prototype = {
|
|||
};
|
||||
|
||||
/**
|
||||
* A collection of small wrappers promisifying functions invoking callbacks.
|
||||
* Returns a promise resolved with a listing of all the tabs in the
|
||||
* provided thread client.
|
||||
*/
|
||||
function listTabs(client) {
|
||||
let deferred = promise.defer();
|
||||
|
|
|
@ -131,7 +131,7 @@ function convertLegacyData (legacyData) {
|
|||
let { profilerData, ticksData, recordingDuration } = legacyData;
|
||||
|
||||
// The `profilerData` and `ticksData` stay, but the previously unrecorded
|
||||
// fields just are empty arrays.
|
||||
// fields just are empty arrays or objects.
|
||||
let data = {
|
||||
label: profilerData.profilerLabel,
|
||||
duration: recordingDuration,
|
||||
|
@ -139,6 +139,7 @@ function convertLegacyData (legacyData) {
|
|||
frames: [],
|
||||
memory: [],
|
||||
ticks: ticksData,
|
||||
allocations: { sites: [], timestamps: [], frames: [], counts: [] },
|
||||
profile: profilerData.profile
|
||||
};
|
||||
|
||||
|
|
|
@ -28,14 +28,16 @@ RecordingModel.prototype = {
|
|||
_recording: false,
|
||||
_profilerStartTime: 0,
|
||||
_timelineStartTime: 0,
|
||||
_memoryStartTime: 0,
|
||||
|
||||
// Serializable fields, necessary and sufficient for import and export.
|
||||
_label: "",
|
||||
_duration: 0,
|
||||
_markers: null,
|
||||
_frames: null,
|
||||
_ticks: null,
|
||||
_memory: null,
|
||||
_ticks: null,
|
||||
_allocations: null,
|
||||
_profile: null,
|
||||
|
||||
/**
|
||||
|
@ -54,6 +56,7 @@ RecordingModel.prototype = {
|
|||
this._frames = recordingData.frames;
|
||||
this._memory = recordingData.memory;
|
||||
this._ticks = recordingData.ticks;
|
||||
this._allocations = recordingData.allocations;
|
||||
this._profile = recordingData.profile;
|
||||
}),
|
||||
|
||||
|
@ -72,8 +75,7 @@ RecordingModel.prototype = {
|
|||
* Starts recording with the PerformanceFront.
|
||||
*
|
||||
* @param object options
|
||||
* An options object to pass to the timeline front. Supported
|
||||
* properties are `withTicks` and `withMemory`.
|
||||
* @see PerformanceFront.prototype.startRecording
|
||||
*/
|
||||
startRecording: Task.async(function *(options = {}) {
|
||||
// Times must come from the actor in order to be self-consistent.
|
||||
|
@ -85,19 +87,24 @@ RecordingModel.prototype = {
|
|||
let info = yield this._front.startRecording(options);
|
||||
this._profilerStartTime = info.profilerStartTime;
|
||||
this._timelineStartTime = info.timelineStartTime;
|
||||
this._memoryStartTime = info.memoryStartTime;
|
||||
this._recording = true;
|
||||
|
||||
this._markers = [];
|
||||
this._frames = [];
|
||||
this._memory = [];
|
||||
this._ticks = [];
|
||||
this._allocations = { sites: [], timestamps: [], frames: [], counts: [] };
|
||||
}),
|
||||
|
||||
/**
|
||||
* Stops recording with the PerformanceFront.
|
||||
*
|
||||
* @param object options
|
||||
* @see RecordingModel.prototype.startRecording
|
||||
*/
|
||||
stopRecording: Task.async(function *() {
|
||||
let info = yield this._front.stopRecording();
|
||||
stopRecording: Task.async(function *(options) {
|
||||
let info = yield this._front.stopRecording(options);
|
||||
this._profile = info.profile;
|
||||
this._duration = info.profilerEndTime - this._profilerStartTime;
|
||||
this._recording = false;
|
||||
|
@ -168,6 +175,14 @@ RecordingModel.prototype = {
|
|||
return this._ticks;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the memory allocations data in this recording.
|
||||
* @return array
|
||||
*/
|
||||
getAllocations: function() {
|
||||
return this._allocations;
|
||||
},
|
||||
|
||||
/**
|
||||
* Gets the profiler data in this recording.
|
||||
* @return array
|
||||
|
@ -186,8 +201,9 @@ RecordingModel.prototype = {
|
|||
let frames = this.getFrames();
|
||||
let memory = this.getMemory();
|
||||
let ticks = this.getTicks();
|
||||
let allocations = this.getAllocations();
|
||||
let profile = this.getProfile();
|
||||
return { label, duration, markers, frames, memory, ticks, profile };
|
||||
return { label, duration, markers, frames, memory, ticks, allocations, profile };
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -209,35 +225,50 @@ RecordingModel.prototype = {
|
|||
}
|
||||
|
||||
switch (eventName) {
|
||||
// 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":
|
||||
// Accumulate timeline markers into an array. Furthermore, the timestamps
|
||||
// do not have a zero epoch, so offset all of them by the 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":
|
||||
case "frames": {
|
||||
let [, frames] = data;
|
||||
Array.prototype.push.apply(this._frames, frames);
|
||||
break;
|
||||
|
||||
// Accumulate memory measurements into an array. Furthermore, the
|
||||
// timestamp does not have a zero epoch, so offset it.
|
||||
case "memory":
|
||||
}
|
||||
// Accumulate memory measurements into an array. Furthermore, the timestamp
|
||||
// does not have a zero epoch, so offset it by the actor's start time.
|
||||
case "memory": {
|
||||
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":
|
||||
case "ticks": {
|
||||
let [, timestamps] = data;
|
||||
this._ticks = timestamps;
|
||||
break;
|
||||
}
|
||||
// Accumulate allocation sites into an array. Furthermore, the timestamps
|
||||
// do not have a zero epoch, and are microseconds instead of milliseconds,
|
||||
// so offset all of them by the start time, also converting from µs to ms.
|
||||
case "allocations": {
|
||||
let [{ sites, timestamps, frames, counts }] = data;
|
||||
let timeOffset = this._memoryStartTime * 1000;
|
||||
let timeScale = 1000;
|
||||
RecordingUtils.offsetAndScaleTimestamps(timestamps, timeOffset, timeScale);
|
||||
Array.prototype.push.apply(this._allocations.sites, sites);
|
||||
Array.prototype.push.apply(this._allocations.timestamps, timestamps);
|
||||
Array.prototype.push.apply(this._allocations.frames, frames);
|
||||
Array.prototype.push.apply(this._allocations.counts, counts);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -58,6 +58,24 @@ exports.RecordingUtils.offsetMarkerTimes = function(markers, timeOffset) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Offsets and scales all the timestamps in the provided array by the
|
||||
* specified time and scale factor.
|
||||
*
|
||||
* @param array array
|
||||
* A list of timestamps received from the backend.
|
||||
* @param number timeOffset
|
||||
* The amount of time to offset by (in milliseconds).
|
||||
* @param number timeScale
|
||||
* The factor to scale by, after offsetting.
|
||||
*/
|
||||
exports.RecordingUtils.offsetAndScaleTimestamps = function(timestamps, timeOffset, timeScale) {
|
||||
for (let i = 0, len = timestamps.length; i < len; i++) {
|
||||
timestamps[i] -= timeOffset;
|
||||
timestamps[i] /= timeScale;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts allocation data from the memory actor to something that follows
|
||||
* the same structure as the samples data received from the profiler.
|
||||
|
|
|
@ -58,7 +58,7 @@ PerformancePanel.prototype = {
|
|||
}
|
||||
|
||||
// Destroy the connection to ensure packet handlers are removed from client.
|
||||
this._connection.destroy();
|
||||
yield this._connection.destroy();
|
||||
|
||||
yield this.panelWin.shutdownPerformance();
|
||||
this.emit("destroyed");
|
||||
|
|
|
@ -21,6 +21,8 @@ devtools.lazyRequireGetter(this, "TIMELINE_BLUEPRINT",
|
|||
"devtools/timeline/global", true);
|
||||
devtools.lazyRequireGetter(this, "L10N",
|
||||
"devtools/profiler/global", true);
|
||||
devtools.lazyRequireGetter(this, "RecordingUtils",
|
||||
"devtools/performance/recording-utils", true);
|
||||
devtools.lazyRequireGetter(this, "RecordingModel",
|
||||
"devtools/performance/recording-model", true);
|
||||
devtools.lazyRequireGetter(this, "MarkersOverview",
|
||||
|
@ -98,14 +100,20 @@ const EVENTS = {
|
|||
// Emitted by the DetailsView when a subview is selected
|
||||
DETAILS_VIEW_SELECTED: "Performance:UI:DetailsViewSelected",
|
||||
|
||||
// Emitted by the CallTreeView when a call tree has been rendered
|
||||
CALL_TREE_RENDERED: "Performance:UI:CallTreeRendered",
|
||||
|
||||
// Emitted by the WaterfallView when it has been rendered
|
||||
WATERFALL_RENDERED: "Performance:UI:WaterfallRendered",
|
||||
|
||||
// Emitted by the FlameGraphView when it has been rendered
|
||||
FLAMEGRAPH_RENDERED: "Performance:UI:FlameGraphRendered",
|
||||
// Emitted by the JsCallTreeView when a call tree has been rendered
|
||||
JS_CALL_TREE_RENDERED: "Performance:UI:JsCallTreeRendered",
|
||||
|
||||
// Emitted by the JsFlameGraphView when it has been rendered
|
||||
JS_FLAMEGRAPH_RENDERED: "Performance:UI:JsFlameGraphRendered",
|
||||
|
||||
// Emitted by the MemoryCallTreeView when a call tree has been rendered
|
||||
MEMORY_CALL_TREE_RENDERED: "Performance:UI:MemoryCallTreeRendered",
|
||||
|
||||
// Emitted by the MemoryFlameGraphView when it has been rendered
|
||||
MEMORY_FLAMEGRAPH_RENDERED: "Performance:UI:MemoryFlameGraphRendered",
|
||||
|
||||
// When a source is shown in the JavaScript Debugger at a specific location.
|
||||
SOURCE_SHOWN_IN_JS_DEBUGGER: "Performance:UI:SourceShownInJsDebugger",
|
||||
|
@ -184,10 +192,11 @@ let PerformanceController = {
|
|||
RecordingsView.on(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||
RecordingsView.on(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||
|
||||
gFront.on("ticks", this._onTimelineData); // framerate
|
||||
gFront.on("markers", this._onTimelineData); // timeline markers
|
||||
gFront.on("frames", this._onTimelineData); // stack frames
|
||||
gFront.on("memory", this._onTimelineData); // memory measurements
|
||||
gFront.on("ticks", this._onTimelineData); // framerate
|
||||
gFront.on("allocations", this._onTimelineData); // memory allocations
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -201,10 +210,11 @@ let PerformanceController = {
|
|||
RecordingsView.off(EVENTS.UI_EXPORT_RECORDING, this.exportRecording);
|
||||
RecordingsView.off(EVENTS.RECORDING_SELECTED, this._onRecordingSelectFromView);
|
||||
|
||||
gFront.off("ticks", this._onTimelineData);
|
||||
gFront.off("markers", this._onTimelineData);
|
||||
gFront.off("frames", this._onTimelineData);
|
||||
gFront.off("memory", this._onTimelineData);
|
||||
gFront.off("ticks", this._onTimelineData);
|
||||
gFront.off("allocations", this._onTimelineData);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -223,7 +233,11 @@ let PerformanceController = {
|
|||
let recording = this._createRecording();
|
||||
|
||||
this.emit(EVENTS.RECORDING_WILL_START, recording);
|
||||
yield recording.startRecording({ withTicks: true, withMemory: true });
|
||||
yield recording.startRecording({
|
||||
withTicks: true,
|
||||
withMemory: true,
|
||||
withAllocations: true
|
||||
});
|
||||
this.emit(EVENTS.RECORDING_STARTED, recording);
|
||||
|
||||
this.setCurrentRecording(recording);
|
||||
|
@ -237,7 +251,9 @@ let PerformanceController = {
|
|||
let recording = this._getLatestRecording();
|
||||
|
||||
this.emit(EVENTS.RECORDING_WILL_STOP, recording);
|
||||
yield recording.stopRecording();
|
||||
yield recording.stopRecording({
|
||||
withAllocations: true
|
||||
});
|
||||
this.emit(EVENTS.RECORDING_STOPPED, recording);
|
||||
}),
|
||||
|
||||
|
@ -318,7 +334,7 @@ let PerformanceController = {
|
|||
* Fired whenever the PerformanceFront emits markers, memory or ticks.
|
||||
*/
|
||||
_onTimelineData: function (...data) {
|
||||
this._recordings.forEach(profile => profile.addTimelineData.apply(profile, data));
|
||||
this._recordings.forEach(e => e.addTimelineData.apply(e, data));
|
||||
this.emit(EVENTS.TIMELINE_DATA, ...data);
|
||||
},
|
||||
|
||||
|
|
|
@ -20,9 +20,11 @@
|
|||
<script type="application/javascript" src="performance/views/overview.js"/>
|
||||
<script type="application/javascript" src="performance/views/toolbar.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-subview.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-call-tree.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-waterfall.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-flamegraph.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-js-call-tree.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-js-flamegraph.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-memory-call-tree.js"/>
|
||||
<script type="application/javascript" src="performance/views/details-memory-flamegraph.js"/>
|
||||
<script type="application/javascript" src="performance/views/details.js"/>
|
||||
<script type="application/javascript" src="performance/views/recordings.js"/>
|
||||
|
||||
|
@ -60,14 +62,25 @@
|
|||
<toolbar id="performance-toolbar" class="devtools-toolbar">
|
||||
<hbox id="performance-toolbar-controls-detail-views" class="devtools-toolbarbutton-group">
|
||||
<toolbarbutton id="select-waterfall-view"
|
||||
class="devtools-toolbarbutton"
|
||||
class="devtools-toolbarbutton devtools-button"
|
||||
label="&profilerUI.toolbar.waterfall;"
|
||||
data-view="waterfall" />
|
||||
<toolbarbutton id="select-calltree-view"
|
||||
class="devtools-toolbarbutton"
|
||||
data-view="calltree" />
|
||||
<toolbarbutton id="select-flamegraph-view"
|
||||
class="devtools-toolbarbutton"
|
||||
data-view="flamegraph" />
|
||||
<toolbarbutton id="select-js-calltree-view"
|
||||
class="devtools-toolbarbutton devtools-button"
|
||||
label="&profilerUI.toolbar.js-calltree;"
|
||||
data-view="js-calltree" />
|
||||
<toolbarbutton id="select-js-flamegraph-view"
|
||||
class="devtools-toolbarbutton devtools-button"
|
||||
label="&profilerUI.toolbar.js-flamegraph;"
|
||||
data-view="js-flamegraph" />
|
||||
<toolbarbutton id="select-memory-calltree-view"
|
||||
class="devtools-toolbarbutton devtools-button"
|
||||
label="&profilerUI.toolbar.memory-calltree;"
|
||||
data-view="memory-calltree" />
|
||||
<toolbarbutton id="select-memory-flamegraph-view"
|
||||
class="devtools-toolbarbutton devtools-button"
|
||||
label="&profilerUI.toolbar.memory-flamegraph;"
|
||||
data-view="memory-flamegraph" />
|
||||
</hbox>
|
||||
<spacer flex="1"></spacer>
|
||||
<hbox id="performance-toolbar-control-options" class="devtools-toolbarbutton-group">
|
||||
|
@ -94,12 +107,12 @@
|
|||
height="150"/>
|
||||
</hbox>
|
||||
|
||||
<vbox id="calltree-view" flex="1">
|
||||
<vbox id="js-calltree-view" flex="1">
|
||||
<hbox class="call-tree-headers-container">
|
||||
<label class="plain call-tree-header"
|
||||
type="duration"
|
||||
crop="end"
|
||||
value="&profilerUI.table.totalDuration;"/>
|
||||
value="&profilerUI.table.totalDuration2;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="percentage"
|
||||
crop="end"
|
||||
|
@ -107,7 +120,7 @@
|
|||
<label class="plain call-tree-header"
|
||||
type="self-duration"
|
||||
crop="end"
|
||||
value="&profilerUI.table.selfDuration;"/>
|
||||
value="&profilerUI.table.selfDuration2;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="self-percentage"
|
||||
crop="end"
|
||||
|
@ -123,9 +136,53 @@
|
|||
</hbox>
|
||||
<vbox class="call-tree-cells-container" flex="1"/>
|
||||
</vbox>
|
||||
<hbox id="flamegraph-view" flex="1">
|
||||
|
||||
<hbox id="js-flamegraph-view" flex="1">
|
||||
</hbox>
|
||||
|
||||
<vbox id="memory-calltree-view" flex="1">
|
||||
<hbox class="call-tree-headers-container">
|
||||
<label class="plain call-tree-header"
|
||||
type="duration"
|
||||
crop="end"
|
||||
value="&profilerUI.table.totalDuration2;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="percentage"
|
||||
crop="end"
|
||||
value="&profilerUI.table.totalPercentage;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="allocations"
|
||||
crop="end"
|
||||
value="&profilerUI.table.totalAlloc;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="self-duration"
|
||||
crop="end"
|
||||
value="&profilerUI.table.selfDuration2;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="self-percentage"
|
||||
crop="end"
|
||||
value="&profilerUI.table.selfPercentage;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="self-allocations"
|
||||
crop="end"
|
||||
value="&profilerUI.table.selfAlloc;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="samples"
|
||||
crop="end"
|
||||
value="&profilerUI.table.samples;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="function"
|
||||
crop="end"
|
||||
value="&profilerUI.table.function;"/>
|
||||
</hbox>
|
||||
<vbox class="call-tree-cells-container" flex="1"/>
|
||||
</vbox>
|
||||
|
||||
<hbox id="memory-flamegraph-view" flex="1">
|
||||
<!-- TODO: bug 1077461 -->
|
||||
</hbox>
|
||||
</deck>
|
||||
|
||||
</vbox>
|
||||
</hbox>
|
||||
</window>
|
||||
|
|
|
@ -14,6 +14,8 @@ support-files =
|
|||
[browser_perf-data-samples.js]
|
||||
[browser_perf-details-calltree-render.js]
|
||||
[browser_perf-details-flamegraph-render.js]
|
||||
[browser_perf-details-memory-calltree-render.js]
|
||||
[browser_perf-details-memory-flamegraph-render.js]
|
||||
[browser_perf-details-waterfall-render.js]
|
||||
[browser_perf-details-01.js]
|
||||
[browser_perf-details-02.js]
|
||||
|
@ -28,7 +30,8 @@ support-files =
|
|||
[browser_perf-front.js]
|
||||
[browser_perf-jump-to-debugger-01.js]
|
||||
[browser_perf-jump-to-debugger-02.js]
|
||||
[browser_perf-options-invert-call-tree.js]
|
||||
[browser_perf-options-invert-call-tree-01.js]
|
||||
[browser_perf-options-invert-call-tree-02.js]
|
||||
[browser_perf-overview-render-01.js]
|
||||
[browser_perf-overview-render-02.js]
|
||||
[browser_perf-overview-render-03.js]
|
||||
|
|
|
@ -13,10 +13,17 @@ function spawnTest () {
|
|||
|
||||
// Select calltree view
|
||||
let viewChanged = onceSpread(DetailsView, EVENTS.DETAILS_VIEW_SELECTED);
|
||||
command($("toolbarbutton[data-view='calltree']"));
|
||||
command($("toolbarbutton[data-view='js-calltree']"));
|
||||
let [_, viewName] = yield viewChanged;
|
||||
is(viewName, "calltree", "DETAILS_VIEW_SELECTED fired with view name");
|
||||
checkViews(DetailsView, doc, "calltree");
|
||||
is(viewName, "js-calltree", "DETAILS_VIEW_SELECTED fired with view name");
|
||||
checkViews(DetailsView, doc, "js-calltree");
|
||||
|
||||
// Select flamegraph view
|
||||
viewChanged = onceSpread(DetailsView, EVENTS.DETAILS_VIEW_SELECTED);
|
||||
command($("toolbarbutton[data-view='js-flamegraph']"));
|
||||
[_, viewName] = yield viewChanged;
|
||||
is(viewName, "js-flamegraph", "DETAILS_VIEW_SELECTED fired with view name");
|
||||
checkViews(DetailsView, doc, "js-flamegraph");
|
||||
|
||||
// Select waterfall view
|
||||
viewChanged = onceSpread(DetailsView, EVENTS.DETAILS_VIEW_SELECTED);
|
||||
|
@ -25,13 +32,6 @@ function spawnTest () {
|
|||
is(viewName, "waterfall", "DETAILS_VIEW_SELECTED fired with view name");
|
||||
checkViews(DetailsView, doc, "waterfall");
|
||||
|
||||
// Select flamegraph view
|
||||
viewChanged = onceSpread(DetailsView, EVENTS.DETAILS_VIEW_SELECTED);
|
||||
command($("toolbarbutton[data-view='flamegraph']"));
|
||||
[_, viewName] = yield viewChanged;
|
||||
is(viewName, "flamegraph", "DETAILS_VIEW_SELECTED fired with view name");
|
||||
checkViews(DetailsView, doc, "flamegraph");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
||||
|
|
|
@ -7,25 +7,25 @@
|
|||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, DetailsView } = panel.panelWin;
|
||||
let { WaterfallView, CallTreeView, FlameGraphView } = panel.panelWin;
|
||||
let { WaterfallView, JsCallTreeView, JsFlameGraphView } = panel.panelWin;
|
||||
|
||||
ok(DetailsView.isViewSelected(WaterfallView),
|
||||
"The waterfall view is selected by default in the details view.");
|
||||
|
||||
let selected = DetailsView.whenViewSelected(CallTreeView);
|
||||
let selected = DetailsView.whenViewSelected(JsCallTreeView);
|
||||
let notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
|
||||
DetailsView.selectView("calltree");
|
||||
DetailsView.selectView("js-calltree");
|
||||
yield Promise.all([selected, notified]);
|
||||
|
||||
ok(DetailsView.isViewSelected(CallTreeView),
|
||||
ok(DetailsView.isViewSelected(JsCallTreeView),
|
||||
"The waterfall view is now selected in the details view.");
|
||||
|
||||
selected = DetailsView.whenViewSelected(FlameGraphView);
|
||||
selected = DetailsView.whenViewSelected(JsFlameGraphView);
|
||||
notified = DetailsView.once(EVENTS.DETAILS_VIEW_SELECTED);
|
||||
DetailsView.selectView("flamegraph");
|
||||
DetailsView.selectView("js-flamegraph");
|
||||
yield Promise.all([selected, notified]);
|
||||
|
||||
ok(DetailsView.isViewSelected(FlameGraphView),
|
||||
ok(DetailsView.isViewSelected(JsFlameGraphView),
|
||||
"The flamegraph view is now selected in the details view.");
|
||||
|
||||
selected = DetailsView.whenViewSelected(WaterfallView);
|
||||
|
|
|
@ -6,28 +6,28 @@
|
|||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, DetailsView, CallTreeView } = panel.panelWin;
|
||||
let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
|
||||
|
||||
DetailsView.selectView("calltree");
|
||||
ok(DetailsView.isViewSelected(CallTreeView), "The call tree is now selected.");
|
||||
DetailsView.selectView("js-calltree");
|
||||
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
let rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
|
||||
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "CallTreeView rendered after recording is stopped.");
|
||||
ok(true, "JsCallTreeView rendered after recording is stopped.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
|
||||
rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "CallTreeView rendered again after recording completed a second time.");
|
||||
ok(true, "JsCallTreeView rendered again after recording completed a second time.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
|
|
@ -6,28 +6,28 @@
|
|||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, DetailsView, FlameGraphView } = panel.panelWin;
|
||||
let { EVENTS, DetailsView, JsFlameGraphView } = panel.panelWin;
|
||||
|
||||
DetailsView.selectView("flamegraph");
|
||||
ok(DetailsView.isViewSelected(FlameGraphView), "The flamegraph is now selected.");
|
||||
DetailsView.selectView("js-flamegraph");
|
||||
ok(DetailsView.isViewSelected(JsFlameGraphView), "The flamegraph is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
let rendered = once(FlameGraphView, EVENTS.FLAMEGRAPH_RENDERED);
|
||||
let rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "FlameGraphView rendered after recording is stopped.");
|
||||
ok(true, "JsFlameGraphView rendered after recording is stopped.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
rendered = once(FlameGraphView, EVENTS.FLAMEGRAPH_RENDERED);
|
||||
rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "FlameGraphView rendered again after recording completed a second time.");
|
||||
ok(true, "JsFlameGraphView rendered again after recording completed a second time.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the memory call tree view renders content after recording.
|
||||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, DetailsView, MemoryCallTreeView } = panel.panelWin;
|
||||
|
||||
DetailsView.selectView("memory-calltree");
|
||||
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "MemoryCallTreeView rendered after recording is stopped.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "MemoryCallTreeView rendered again after recording completed a second time.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/**
|
||||
* Tests that the memory flamegraph view renders content after recording.
|
||||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, DetailsView, MemoryFlameGraphView } = panel.panelWin;
|
||||
|
||||
DetailsView.selectView("memory-flamegraph");
|
||||
ok(DetailsView.isViewSelected(MemoryFlameGraphView), "The flamegraph is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
let rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "MemoryFlameGraphView rendered after recording is stopped.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
rendered = once(MemoryFlameGraphView, EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "MemoryFlameGraphView rendered again after recording completed a second time.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
|
@ -10,26 +10,44 @@ let WAIT_TIME = 1000;
|
|||
function spawnTest () {
|
||||
let { target, front } = yield initBackend(SIMPLE_URL);
|
||||
|
||||
let { profilerStartTime, timelineStartTime } = yield front.startRecording();
|
||||
let {
|
||||
profilerStartTime,
|
||||
timelineStartTime,
|
||||
memoryStartTime
|
||||
} = yield front.startRecording({
|
||||
withAllocations: true
|
||||
});
|
||||
|
||||
ok(typeof profilerStartTime === "number",
|
||||
"The front.startRecording() emits a profiler start time.");
|
||||
ok(typeof timelineStartTime === "number",
|
||||
"The front.startRecording() emits a timeline start time.");
|
||||
ok(typeof memoryStartTime === "number",
|
||||
"The front.startRecording() emits a memory start time.");
|
||||
|
||||
yield busyWait(WAIT_TIME);
|
||||
|
||||
let { profilerEndTime, timelineEndTime } = yield front.stopRecording();
|
||||
let {
|
||||
profilerEndTime,
|
||||
timelineEndTime,
|
||||
memoryEndTime
|
||||
} = yield front.stopRecording({
|
||||
withAllocations: true
|
||||
});
|
||||
|
||||
ok(typeof profilerEndTime === "number",
|
||||
"The front.stopRecording() emits a profiler end time.");
|
||||
ok(typeof timelineEndTime === "number",
|
||||
"The front.stopRecording() emits a timeline end time.");
|
||||
ok(typeof memoryEndTime === "number",
|
||||
"The front.stopRecording() emits a memory end time.");
|
||||
|
||||
ok(profilerEndTime > profilerStartTime,
|
||||
"The profilerEndTime is after profilerStartTime.");
|
||||
ok(timelineEndTime > timelineStartTime,
|
||||
"The timelineEndTime is after timelineStartTime.");
|
||||
ok(memoryEndTime > memoryStartTime,
|
||||
"The memoryEndTime is after memoryStartTime.");
|
||||
|
||||
yield removeTab(target.tab);
|
||||
finish();
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const INVERT_PREF = "devtools.performance.ui.invert-call-tree";
|
||||
|
||||
/**
|
||||
* Tests that the js call tree view is re-rendered after the
|
||||
* "invert-call-tree" pref is changed.
|
||||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, DetailsView, JsCallTreeView } = panel.panelWin;
|
||||
|
||||
Services.prefs.setBoolPref(INVERT_PREF, true);
|
||||
|
||||
DetailsView.selectView("js-calltree");
|
||||
ok(DetailsView.isViewSelected(JsCallTreeView), "The call tree is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
let rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
Services.prefs.setBoolPref(INVERT_PREF, false);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "JsCallTreeView rerendered when toggling invert-call-tree.");
|
||||
|
||||
rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
Services.prefs.setBoolPref(INVERT_PREF, true);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "JsCallTreeView rerendered when toggling back invert-call-tree.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const INVERT_PREF = "devtools.performance.ui.invert-call-tree";
|
||||
|
||||
/**
|
||||
* Tests that the memory call tree view is re-rendered after the
|
||||
* "invert-call-tree" pref is changed.
|
||||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, DetailsView, MemoryCallTreeView } = panel.panelWin;
|
||||
|
||||
Services.prefs.setBoolPref(INVERT_PREF, true);
|
||||
|
||||
DetailsView.selectView("memory-calltree");
|
||||
ok(DetailsView.isViewSelected(MemoryCallTreeView), "The call tree is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
let rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
|
||||
Services.prefs.setBoolPref(INVERT_PREF, false);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "MemoryCallTreeView rerendered when toggling invert-call-tree.");
|
||||
|
||||
rendered = once(MemoryCallTreeView, EVENTS.MEMORY_CALL_TREE_RENDERED);
|
||||
Services.prefs.setBoolPref(INVERT_PREF, true);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "MemoryCallTreeView rerendered when toggling back invert-call-tree.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
const INVERT_PREF = "devtools.performance.ui.invert-call-tree";
|
||||
|
||||
/**
|
||||
* Tests that the call tree view renders after recording.
|
||||
*/
|
||||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, DetailsView, CallTreeView } = panel.panelWin;
|
||||
|
||||
Services.prefs.setBoolPref(INVERT_PREF, true);
|
||||
|
||||
DetailsView.selectView("calltree");
|
||||
ok(DetailsView.isViewSelected(CallTreeView), "The call tree is now selected.");
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
||||
let rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
|
||||
yield stopRecording(panel);
|
||||
yield rendered;
|
||||
|
||||
rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
|
||||
Services.prefs.setBoolPref(INVERT_PREF, false);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "CallTreeView rerendered when toggling invert-call-tree.");
|
||||
|
||||
rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
|
||||
Services.prefs.setBoolPref(INVERT_PREF, true);
|
||||
yield rendered;
|
||||
|
||||
ok(true, "CallTreeView rerendered when toggling back invert-call-tree.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
}
|
|
@ -53,9 +53,9 @@ function spawnTest () {
|
|||
OverviewView.off(EVENTS.OVERVIEW_RANGE_SELECTED, fail);
|
||||
|
||||
let secondInterval = OverviewView.getTimeInterval();
|
||||
is(secondInterval.startTime, 30,
|
||||
is(Math.round(secondInterval.startTime), 30,
|
||||
"The interval's start time was properly set again.");
|
||||
is(secondInterval.endTime, 40,
|
||||
is(Math.round(secondInterval.endTime), 40,
|
||||
"The interval's end time was properly set again.");
|
||||
|
||||
yield teardown(panel);
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
function spawnTest () {
|
||||
let { panel } = yield initPerformance(SIMPLE_URL);
|
||||
let { EVENTS, PerformanceController, OverviewView, DetailsView } = panel.panelWin;
|
||||
let { WaterfallView, CallTreeView, FlameGraphView } = panel.panelWin;
|
||||
let { WaterfallView, JsCallTreeView, JsFlameGraphView } = panel.panelWin;
|
||||
|
||||
let updatedWaterfall = 0;
|
||||
let updatedCallTree = 0;
|
||||
let updatedFlameGraph = 0;
|
||||
WaterfallView.on(EVENTS.WATERFALL_RENDERED, () => updatedWaterfall++);
|
||||
CallTreeView.on(EVENTS.CALL_TREE_RENDERED, () => updatedCallTree++);
|
||||
FlameGraphView.on(EVENTS.FLAMEGRAPH_RENDERED, () => updatedFlameGraph++);
|
||||
JsCallTreeView.on(EVENTS.JS_CALL_TREE_RENDERED, () => updatedCallTree++);
|
||||
JsFlameGraphView.on(EVENTS.JS_FLAMEGRAPH_RENDERED, () => updatedFlameGraph++);
|
||||
|
||||
yield startRecording(panel);
|
||||
yield busyWait(100);
|
||||
|
@ -26,23 +26,23 @@ function spawnTest () {
|
|||
yield rendered;
|
||||
ok(true, "Waterfall rerenders when a range in the overview graph is selected.");
|
||||
|
||||
rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
|
||||
DetailsView.selectView("calltree");
|
||||
rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
DetailsView.selectView("js-calltree");
|
||||
yield rendered;
|
||||
ok(true, "Call tree rerenders after its corresponding pane is shown.");
|
||||
|
||||
rendered = once(FlameGraphView, EVENTS.FLAMEGRAPH_RENDERED);
|
||||
DetailsView.selectView("flamegraph");
|
||||
rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
|
||||
DetailsView.selectView("js-flamegraph");
|
||||
yield rendered;
|
||||
ok(true, "Flamegraph rerenders after its corresponding pane is shown.");
|
||||
|
||||
rendered = once(FlameGraphView, EVENTS.FLAMEGRAPH_RENDERED);
|
||||
rendered = once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED);
|
||||
OverviewView.emit(EVENTS.OVERVIEW_RANGE_CLEARED);
|
||||
yield rendered;
|
||||
ok(true, "Flamegraph rerenders when a range in the overview graph is removed.");
|
||||
|
||||
rendered = once(CallTreeView, EVENTS.CALL_TREE_RENDERED);
|
||||
DetailsView.selectView("calltree");
|
||||
rendered = once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED);
|
||||
DetailsView.selectView("js-calltree");
|
||||
yield rendered;
|
||||
ok(true, "Call tree rerenders after its corresponding pane is shown.");
|
||||
|
||||
|
@ -52,8 +52,8 @@ function spawnTest () {
|
|||
ok(true, "Waterfall rerenders after its corresponding pane is shown.");
|
||||
|
||||
is(updatedWaterfall, 3, "WaterfallView rerendered 3 times.");
|
||||
is(updatedCallTree, 2, "CallTreeView rerendered 2 times.");
|
||||
is(updatedFlameGraph, 2, "FlameGraphView rerendered 2 times.");
|
||||
is(updatedCallTree, 2, "JsCallTreeView rerendered 2 times.");
|
||||
is(updatedFlameGraph, 2, "JsFlameGraphView rerendered 2 times.");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
|
|
@ -56,8 +56,10 @@ let test = Task.async(function*() {
|
|||
"The impored data is identical to the original data (4).");
|
||||
is(importedData.ticks.toSource(), originalData.ticks.toSource(),
|
||||
"The impored data is identical to the original data (5).");
|
||||
is(importedData.profile.toSource(), originalData.profile.toSource(),
|
||||
is(importedData.allocations.toSource(), originalData.allocations.toSource(),
|
||||
"The impored data is identical to the original data (6).");
|
||||
is(importedData.profile.toSource(), originalData.profile.toSource(),
|
||||
"The impored data is identical to the original data (7).");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
|
|
@ -54,16 +54,18 @@ let test = Task.async(function*() {
|
|||
"The imported legacy data was successfully converted for the current tool (1).");
|
||||
is(importedData.duration, data.duration,
|
||||
"The imported legacy data was successfully converted for the current tool (2).");
|
||||
is(importedData.markers.toSource(), [].toSource(),
|
||||
is(importedData.markers.toSource(), data.markers.toSource(),
|
||||
"The imported legacy data was successfully converted for the current tool (3).");
|
||||
is(importedData.frames.toSource(), [].toSource(),
|
||||
is(importedData.frames.toSource(), data.frames.toSource(),
|
||||
"The imported legacy data was successfully converted for the current tool (4).");
|
||||
is(importedData.memory.toSource(), [].toSource(),
|
||||
is(importedData.memory.toSource(), data.memory.toSource(),
|
||||
"The imported legacy data was successfully converted for the current tool (5).");
|
||||
is(importedData.ticks.toSource(), data.ticks.toSource(),
|
||||
"The imported legacy data was successfully converted for the current tool (6).");
|
||||
is(importedData.profile.toSource(), data.profile.toSource(),
|
||||
is(importedData.allocations.toSource(), data.allocations.toSource(),
|
||||
"The imported legacy data was successfully converted for the current tool (7).");
|
||||
is(importedData.profile.toSource(), data.profile.toSource(),
|
||||
"The imported legacy data was successfully converted for the current tool (8).");
|
||||
|
||||
yield teardown(panel);
|
||||
finish();
|
||||
|
|
|
@ -298,9 +298,9 @@ function waitForWidgetsRendered(panel) {
|
|||
let {
|
||||
EVENTS,
|
||||
OverviewView,
|
||||
CallTreeView,
|
||||
WaterfallView,
|
||||
FlameGraphView
|
||||
JsCallTreeView,
|
||||
JsFlameGraphView
|
||||
} = panel.panelWin;
|
||||
|
||||
return Promise.all([
|
||||
|
@ -309,8 +309,8 @@ function waitForWidgetsRendered(panel) {
|
|||
once(OverviewView, EVENTS.FRAMERATE_GRAPH_RENDERED),
|
||||
once(OverviewView, EVENTS.OVERVIEW_RENDERED),
|
||||
once(WaterfallView, EVENTS.WATERFALL_RENDERED),
|
||||
once(CallTreeView, EVENTS.CALL_TREE_RENDERED),
|
||||
once(FlameGraphView, EVENTS.FLAMEGRAPH_RENDERED)
|
||||
once(JsCallTreeView, EVENTS.JS_CALL_TREE_RENDERED),
|
||||
once(JsFlameGraphView, EVENTS.JS_FLAMEGRAPH_RENDERED)
|
||||
]);
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
/**
|
||||
* CallTree view containing profiler call tree, controlled by DetailsView.
|
||||
*/
|
||||
let CallTreeView = Heritage.extend(DetailsSubview, {
|
||||
let JsCallTreeView = Heritage.extend(DetailsSubview, {
|
||||
rangeChangeDebounceTime: 50, // ms
|
||||
|
||||
/**
|
||||
|
@ -43,7 +43,7 @@ let CallTreeView = Heritage.extend(DetailsSubview, {
|
|||
let profile = recording.getProfile();
|
||||
let threadNode = this._prepareCallTree(profile, interval, options);
|
||||
this._populateCallTree(threadNode, options);
|
||||
this.emit(EVENTS.CALL_TREE_RENDERED);
|
||||
this.emit(EVENTS.JS_CALL_TREE_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -94,10 +94,13 @@ let CallTreeView = Heritage.extend(DetailsSubview, {
|
|||
root.on("link", this._onLink);
|
||||
|
||||
// Clear out other call trees.
|
||||
let container = $(".call-tree-cells-container");
|
||||
let container = $("#js-calltree-view > .call-tree-cells-container");
|
||||
container.innerHTML = "";
|
||||
root.attachTo(container);
|
||||
|
||||
// Profiler data does not contain memory allocations information.
|
||||
root.toggleAllocations(false);
|
||||
|
||||
// When platform data isn't shown, hide the cateogry labels, since they're
|
||||
// only available for C++ frames.
|
||||
let contentOnly = !Prefs.showPlatformData;
|
|
@ -7,14 +7,14 @@
|
|||
* FlameGraph view containing a pyramid-like visualization of a profile,
|
||||
* controlled by DetailsView.
|
||||
*/
|
||||
let FlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
let JsFlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: Task.async(function* () {
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
this.graph = new FlameGraph($("#flamegraph-view"));
|
||||
this.graph = new FlameGraph($("#js-flamegraph-view"));
|
||||
this.graph.timelineTickUnits = L10N.getStr("graphs.ms");
|
||||
yield this.graph.ready();
|
||||
|
||||
|
@ -61,7 +61,7 @@ let FlameGraphView = Heritage.extend(DetailsSubview, {
|
|||
}
|
||||
});
|
||||
|
||||
this.emit(EVENTS.FLAMEGRAPH_RENDERED);
|
||||
this.emit(EVENTS.JS_FLAMEGRAPH_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
|
@ -0,0 +1,124 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* CallTree view containing memory allocation sites, controlled by DetailsView.
|
||||
*/
|
||||
let MemoryCallTreeView = Heritage.extend(DetailsSubview, {
|
||||
rangeChangeDebounceTime: 100, // ms
|
||||
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: function () {
|
||||
DetailsSubview.initialize.call(this);
|
||||
|
||||
this._cache = new WeakMap();
|
||||
|
||||
this._onPrefChanged = this._onPrefChanged.bind(this);
|
||||
this._onLink = this._onLink.bind(this);
|
||||
|
||||
PerformanceController.on(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
},
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
DetailsSubview.destroy.call(this);
|
||||
|
||||
PerformanceController.off(EVENTS.PREF_CHANGED, this._onPrefChanged);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method for handling all the set up for rendering a new call tree.
|
||||
*
|
||||
* @param object interval [optional]
|
||||
* The { startTime, endTime }, in milliseconds.
|
||||
* @param object options [optional]
|
||||
* Additional options for new the call tree.
|
||||
*/
|
||||
render: function (interval={}, options={}) {
|
||||
let recording = PerformanceController.getCurrentRecording();
|
||||
let allocations = recording.getAllocations();
|
||||
let threadNode = this._prepareCallTree(allocations, interval, options);
|
||||
this._populateCallTree(threadNode, options);
|
||||
this.emit(EVENTS.MEMORY_CALL_TREE_RENDERED);
|
||||
},
|
||||
|
||||
/**
|
||||
* Fired on the "link" event for the call tree in this container.
|
||||
*/
|
||||
_onLink: function (_, treeItem) {
|
||||
let { url, line } = treeItem.frame.getInfo();
|
||||
viewSourceInDebugger(url, line).then(
|
||||
() => this.emit(EVENTS.SOURCE_SHOWN_IN_JS_DEBUGGER),
|
||||
() => this.emit(EVENTS.SOURCE_NOT_FOUND_IN_JS_DEBUGGER));
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when the recording is stopped and prepares data to
|
||||
* populate the call tree.
|
||||
*/
|
||||
_prepareCallTree: function (allocations, { startTime, endTime }, options) {
|
||||
let cached = this._cache.get(allocations);
|
||||
if (cached) {
|
||||
var samples = cached;
|
||||
} else {
|
||||
var samples = RecordingUtils.getSamplesFromAllocations(allocations);
|
||||
this._cache.set(allocations, samples);
|
||||
}
|
||||
|
||||
let contentOnly = !Prefs.showPlatformData;
|
||||
let invertTree = PerformanceController.getPref("invert-call-tree");
|
||||
|
||||
let threadNode = new ThreadNode(samples,
|
||||
{ startTime, endTime, contentOnly, invertTree });
|
||||
|
||||
// If we have an empty profile (no samples), then don't invert the tree, as
|
||||
// it would hide the root node and a completely blank call tree space can be
|
||||
// mis-interpreted as an error.
|
||||
options.inverted = invertTree && threadNode.samples > 0;
|
||||
|
||||
return threadNode;
|
||||
},
|
||||
|
||||
/**
|
||||
* Renders the call tree.
|
||||
*/
|
||||
_populateCallTree: function (frameNode, options={}) {
|
||||
let root = new CallView({
|
||||
frame: frameNode,
|
||||
inverted: options.inverted,
|
||||
// Root nodes are hidden in inverted call trees.
|
||||
hidden: options.inverted,
|
||||
// Memory call trees should be sorted by allocations.
|
||||
sortingPredicate: (a, b) => a.frame.allocations < b.frame.allocations ? 1 : -1,
|
||||
// Call trees should only auto-expand when not inverted. Passing undefined
|
||||
// will default to the CALL_TREE_AUTO_EXPAND depth.
|
||||
autoExpandDepth: options.inverted ? 0 : undefined,
|
||||
});
|
||||
|
||||
// Bind events.
|
||||
root.on("link", this._onLink);
|
||||
|
||||
// Clear out other call trees.
|
||||
let container = $("#memory-calltree-view > .call-tree-cells-container");
|
||||
container.innerHTML = "";
|
||||
root.attachTo(container);
|
||||
|
||||
// Memory allocation samples don't contain cateogry labels.
|
||||
root.toggleCategories(false);
|
||||
},
|
||||
|
||||
/**
|
||||
* Called when a preference under "devtools.performance.ui." is changed.
|
||||
*/
|
||||
_onPrefChanged: function (_, prefName, value) {
|
||||
if (prefName === "invert-call-tree") {
|
||||
this.render(OverviewView.getTimeInterval());
|
||||
}
|
||||
}
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
/* 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";
|
||||
|
||||
/**
|
||||
* FlameGraph view containing a pyramid-like visualization of memory allocation
|
||||
* sites, controlled by DetailsView.
|
||||
*
|
||||
* TODO: bug 1077469
|
||||
*/
|
||||
let MemoryFlameGraphView = Heritage.extend(DetailsSubview, {
|
||||
/**
|
||||
* Sets up the view with event binding.
|
||||
*/
|
||||
initialize: Task.async(function* () {
|
||||
DetailsSubview.initialize.call(this);
|
||||
}),
|
||||
|
||||
/**
|
||||
* Unbinds events.
|
||||
*/
|
||||
destroy: function () {
|
||||
DetailsSubview.destroy.call(this);
|
||||
},
|
||||
|
||||
/**
|
||||
* Method for handling all the set up for rendering a new flamegraph.
|
||||
*
|
||||
* @param object interval [optional]
|
||||
* The { startTime, endTime }, in milliseconds.
|
||||
*/
|
||||
render: function (interval={}) {
|
||||
this.emit(EVENTS.MEMORY_FLAMEGRAPH_RENDERED);
|
||||
}
|
||||
});
|
|
@ -14,9 +14,11 @@ let DetailsView = {
|
|||
* Name to node+object mapping of subviews.
|
||||
*/
|
||||
components: {
|
||||
waterfall: { id: "waterfall-view", view: WaterfallView },
|
||||
calltree: { id: "calltree-view", view: CallTreeView },
|
||||
flamegraph: { id: "flamegraph-view", view: FlameGraphView }
|
||||
"waterfall": { id: "waterfall-view", view: WaterfallView },
|
||||
"js-calltree": { id: "js-calltree-view", view: JsCallTreeView },
|
||||
"js-flamegraph": { id: "js-flamegraph-view", view: JsFlameGraphView },
|
||||
"memory-calltree": { id: "memory-calltree-view", view: MemoryCallTreeView },
|
||||
"memory-flamegraph": { id: "memory-flamegraph-view", view: MemoryFlameGraphView }
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -112,7 +112,7 @@
|
|||
<label class="plain call-tree-header"
|
||||
type="duration"
|
||||
crop="end"
|
||||
value="&profilerUI.table.totalDuration;"/>
|
||||
value="&profilerUI.table.totalDuration2;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="percentage"
|
||||
crop="end"
|
||||
|
@ -120,7 +120,7 @@
|
|||
<label class="plain call-tree-header"
|
||||
type="self-duration"
|
||||
crop="end"
|
||||
value="&profilerUI.table.selfDuration;"/>
|
||||
value="&profilerUI.table.selfDuration2;"/>
|
||||
<label class="plain call-tree-header"
|
||||
type="self-percentage"
|
||||
crop="end"
|
||||
|
|
|
@ -22,14 +22,14 @@ function test() {
|
|||
is(container.childNodes[0].className, "call-tree-item",
|
||||
"The root node in the tree has the correct class name.");
|
||||
|
||||
is(container.childNodes[0].childNodes.length, 6,
|
||||
is(container.childNodes[0].childNodes.length, 8,
|
||||
"The root node in the tree has the correct number of children.");
|
||||
is(container.childNodes[0].querySelectorAll(".call-tree-cell").length, 6,
|
||||
is(container.childNodes[0].querySelectorAll(".call-tree-cell").length, 8,
|
||||
"The root node in the tree has only 'call-tree-cell' children.");
|
||||
|
||||
is(container.childNodes[0].childNodes[0].getAttribute("type"), "duration",
|
||||
"The root node in the tree has a duration cell.");
|
||||
is(container.childNodes[0].childNodes[0].getAttribute("value"), "15",
|
||||
is(container.childNodes[0].childNodes[0].getAttribute("value"), "15 ms",
|
||||
"The root node in the tree has the correct duration cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[1].getAttribute("type"), "percentage",
|
||||
|
@ -37,24 +37,34 @@ function test() {
|
|||
is(container.childNodes[0].childNodes[1].getAttribute("value"), "100%",
|
||||
"The root node in the tree has the correct percentage cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[2].getAttribute("type"), "self-duration",
|
||||
is(container.childNodes[0].childNodes[2].getAttribute("type"), "allocations",
|
||||
"The root node in the tree has a self-duration cell.");
|
||||
is(container.childNodes[0].childNodes[2].getAttribute("value"), "0",
|
||||
"The root node in the tree has the correct self-duration cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[3].getAttribute("type"), "self-percentage",
|
||||
is(container.childNodes[0].childNodes[3].getAttribute("type"), "self-duration",
|
||||
"The root node in the tree has a self-duration cell.");
|
||||
is(container.childNodes[0].childNodes[3].getAttribute("value"), "0 ms",
|
||||
"The root node in the tree has the correct self-duration cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[4].getAttribute("type"), "self-percentage",
|
||||
"The root node in the tree has a self-percentage cell.");
|
||||
is(container.childNodes[0].childNodes[3].getAttribute("value"), "0%",
|
||||
is(container.childNodes[0].childNodes[4].getAttribute("value"), "0%",
|
||||
"The root node in the tree has the correct self-percentage cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[4].getAttribute("type"), "samples",
|
||||
is(container.childNodes[0].childNodes[5].getAttribute("type"), "self-allocations",
|
||||
"The root node in the tree has a self-percentage cell.");
|
||||
is(container.childNodes[0].childNodes[5].getAttribute("value"), "0",
|
||||
"The root node in the tree has the correct self-percentage cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[6].getAttribute("type"), "samples",
|
||||
"The root node in the tree has an samples cell.");
|
||||
is(container.childNodes[0].childNodes[4].getAttribute("value"), "4",
|
||||
is(container.childNodes[0].childNodes[6].getAttribute("value"), "4",
|
||||
"The root node in the tree has the correct samples cell value.");
|
||||
|
||||
is(container.childNodes[0].childNodes[5].getAttribute("type"), "function",
|
||||
is(container.childNodes[0].childNodes[7].getAttribute("type"), "function",
|
||||
"The root node in the tree has a function cell.");
|
||||
is(container.childNodes[0].childNodes[5].style.MozMarginStart, "0px",
|
||||
is(container.childNodes[0].childNodes[7].style.MozMarginStart, "0px",
|
||||
"The root node in the tree has the correct indentation.");
|
||||
|
||||
finish();
|
||||
|
|
|
@ -27,7 +27,7 @@ function test() {
|
|||
is(container.childNodes[0].className, "call-tree-item",
|
||||
"The root node in the tree has the correct class name.");
|
||||
|
||||
is($$dur(0).getAttribute("value"), "15",
|
||||
is($$dur(0).getAttribute("value"), "15 ms",
|
||||
"The root's duration cell displays the correct value.");
|
||||
is($$perc(0).getAttribute("value"), "100%",
|
||||
"The root's percentage cell displays the correct value.");
|
||||
|
@ -53,7 +53,7 @@ function test() {
|
|||
is(container.childNodes[1].className, "call-tree-item",
|
||||
"The .A node in the tree has the correct class name.");
|
||||
|
||||
is($$dur(1).getAttribute("value"), "15",
|
||||
is($$dur(1).getAttribute("value"), "15 ms",
|
||||
"The .A node's duration cell displays the correct value.");
|
||||
is($$perc(1).getAttribute("value"), "100%",
|
||||
"The .A node's percentage cell displays the correct value.");
|
||||
|
@ -82,7 +82,7 @@ function test() {
|
|||
is(container.childNodes[3].className, "call-tree-item",
|
||||
"The .E node in the tree has the correct class name.");
|
||||
|
||||
is($$dur(2).getAttribute("value"), "8",
|
||||
is($$dur(2).getAttribute("value"), "8 ms",
|
||||
"The .A.B node's duration cell displays the correct value.");
|
||||
is($$perc(2).getAttribute("value"), "75%",
|
||||
"The .A.B node's percentage cell displays the correct value.");
|
||||
|
@ -101,7 +101,7 @@ function test() {
|
|||
is($$fun(".call-tree-category")[2].getAttribute("value"), "Styles",
|
||||
"The .A.B node's function cell displays the correct category.");
|
||||
|
||||
is($$dur(3).getAttribute("value"), "7",
|
||||
is($$dur(3).getAttribute("value"), "7 ms",
|
||||
"The .A.E node's duration cell displays the correct value.");
|
||||
is($$perc(3).getAttribute("value"), "25%",
|
||||
"The .A.E node's percentage cell displays the correct value.");
|
||||
|
|
|
@ -55,19 +55,19 @@ function test() {
|
|||
is($$name(6).getAttribute("value"), "F",
|
||||
"The .A.E.F node's function cell displays the correct name.");
|
||||
|
||||
is($$duration(0).getAttribute("value"), "15",
|
||||
is($$duration(0).getAttribute("value"), "15 ms",
|
||||
"The root node's function cell displays the correct duration.");
|
||||
is($$duration(1).getAttribute("value"), "15",
|
||||
is($$duration(1).getAttribute("value"), "15 ms",
|
||||
"The .A node's function cell displays the correct duration.");
|
||||
is($$duration(2).getAttribute("value"), "8",
|
||||
is($$duration(2).getAttribute("value"), "8 ms",
|
||||
"The .A.B node's function cell displays the correct duration.");
|
||||
is($$duration(3).getAttribute("value"), "3",
|
||||
is($$duration(3).getAttribute("value"), "3 ms",
|
||||
"The .A.B.D node's function cell displays the correct duration.");
|
||||
is($$duration(4).getAttribute("value"), "5",
|
||||
is($$duration(4).getAttribute("value"), "5 ms",
|
||||
"The .A.B.C node's function cell displays the correct duration.");
|
||||
is($$duration(5).getAttribute("value"), "7",
|
||||
is($$duration(5).getAttribute("value"), "7 ms",
|
||||
"The .A.E node's function cell displays the correct duration.");
|
||||
is($$duration(6).getAttribute("value"), "7",
|
||||
is($$duration(6).getAttribute("value"), "7 ms",
|
||||
"The .A.E.F node's function cell displays the correct duration.");
|
||||
|
||||
finish();
|
||||
|
|
|
@ -42,22 +42,26 @@ function test() {
|
|||
ok(!A.target.querySelector(".call-tree-category").hidden,
|
||||
"The .A.B.D node's category label cell should not be hidden.");
|
||||
|
||||
is(D.target.childNodes.length, 6,
|
||||
is(D.target.childNodes.length, 8,
|
||||
"The number of columns displayed for tree items is correct.");
|
||||
is(D.target.childNodes[0].getAttribute("type"), "duration",
|
||||
"The first column displayed for tree items is correct.");
|
||||
is(D.target.childNodes[1].getAttribute("type"), "percentage",
|
||||
"The third column displayed for tree items is correct.");
|
||||
is(D.target.childNodes[2].getAttribute("type"), "self-duration",
|
||||
is(D.target.childNodes[2].getAttribute("type"), "allocations",
|
||||
"The second column displayed for tree items is correct.");
|
||||
is(D.target.childNodes[3].getAttribute("type"), "self-percentage",
|
||||
is(D.target.childNodes[3].getAttribute("type"), "self-duration",
|
||||
"The second column displayed for tree items is correct.");
|
||||
is(D.target.childNodes[4].getAttribute("type"), "self-percentage",
|
||||
"The fourth column displayed for tree items is correct.");
|
||||
is(D.target.childNodes[4].getAttribute("type"), "samples",
|
||||
is(D.target.childNodes[5].getAttribute("type"), "self-allocations",
|
||||
"The fourth column displayed for tree items is correct.");
|
||||
is(D.target.childNodes[6].getAttribute("type"), "samples",
|
||||
"The fifth column displayed for tree items is correct.");
|
||||
is(D.target.childNodes[5].getAttribute("type"), "function",
|
||||
is(D.target.childNodes[7].getAttribute("type"), "function",
|
||||
"The sixth column displayed for tree items is correct.");
|
||||
|
||||
let functionCell = D.target.childNodes[5];
|
||||
let functionCell = D.target.childNodes[7];
|
||||
|
||||
is(functionCell.childNodes.length, 9,
|
||||
"The number of columns displayed for function cells is correct.");
|
||||
|
|
|
@ -509,6 +509,7 @@ let ProfileView = {
|
|||
|
||||
let contentOnly = !Prefs.showPlatformData;
|
||||
callTreeRoot.toggleCategories(!contentOnly);
|
||||
callTreeRoot.toggleAllocations(false);
|
||||
|
||||
this._callTreeRootByPanel.set(panel, callTreeRoot);
|
||||
},
|
||||
|
|
|
@ -140,12 +140,15 @@ ThreadNode.prototype = {
|
|||
* The column number inside the source containing this function call.
|
||||
* @param number category
|
||||
* The category type of this function call ("js", "graphics" etc.).
|
||||
* @param number allocations
|
||||
* The number of memory allocations performed in this frame.
|
||||
*/
|
||||
function FrameNode({ location, line, column, category }) {
|
||||
function FrameNode({ location, line, column, category, allocations }) {
|
||||
this.location = location;
|
||||
this.line = line;
|
||||
this.column = column;
|
||||
this.category = category;
|
||||
this.allocations = allocations || 0;
|
||||
this.sampleTimes = [];
|
||||
this.samples = 0;
|
||||
this.duration = 0;
|
||||
|
|
|
@ -13,10 +13,13 @@ loader.lazyImporter(this, "Heritage",
|
|||
loader.lazyImporter(this, "AbstractTreeItem",
|
||||
"resource:///modules/devtools/AbstractTreeItem.jsm");
|
||||
|
||||
const MILLISECOND_UNITS = L10N.getStr("table.ms");
|
||||
const PERCENTAGE_UNITS = L10N.getStr("table.percentage");
|
||||
const URL_LABEL_TOOLTIP = L10N.getStr("table.url.tooltiptext");
|
||||
const ZOOM_BUTTON_TOOLTIP = L10N.getStr("table.zoom.tooltiptext");
|
||||
const CALL_TREE_INDENTATION = 16; // px
|
||||
const CALL_TREE_AUTO_EXPAND = 3; // depth
|
||||
const CALL_TREE_INDENTATION = 16; // px
|
||||
const DEFAULT_SORTING_PREDICATE = (a, b) => a.frame.samples < b.frame.samples ? 1 : -1;
|
||||
|
||||
const clamp = (val, min, max) => Math.max(min, Math.min(max, val));
|
||||
const sum = vals => vals.reduce((a, b) => a + b, 0);
|
||||
|
@ -37,10 +40,6 @@ exports.CallView = CallView;
|
|||
* Every instance of a `CallView` represents a row in the call tree. The same
|
||||
* parent node is used for all rows.
|
||||
*
|
||||
* @param number autoExpandDepth [optional]
|
||||
* The depth to which the tree should automatically expand. Defualts to
|
||||
* the caller's autoExpandDepth if a caller exists, otherwise defaults to
|
||||
* CALL_TREE_AUTO_EXPAND.
|
||||
* @param CallView caller
|
||||
* The CallView considered the "caller" frame. This instance will be
|
||||
* represent the "callee". Should be null for root nodes.
|
||||
|
@ -54,9 +53,17 @@ exports.CallView = CallView;
|
|||
* @param boolean inverted [optional]
|
||||
* Whether the call tree has been inverted (bottom up, rather than
|
||||
* top-down). Defaults to false.
|
||||
* @param function sortingPredicate [optional]
|
||||
* The predicate used to sort the tree items when created. Defaults to
|
||||
* the caller's sortingPredicate if a caller exists, otherwise defaults
|
||||
* to DEFAULT_SORTING_PREDICATE. The two passed arguments are FrameNodes.
|
||||
* @param number autoExpandDepth [optional]
|
||||
* The depth to which the tree should automatically expand. Defualts to
|
||||
* the caller's `autoExpandDepth` if a caller exists, otherwise defaults
|
||||
* to CALL_TREE_AUTO_EXPAND.
|
||||
*/
|
||||
function CallView({ autoExpandDepth, caller, frame, level, hidden, inverted }) {
|
||||
// Assume no indentation if the this tree item's level is not specified.
|
||||
function CallView({ caller, frame, level, hidden, inverted, sortingPredicate, autoExpandDepth }) {
|
||||
// Assume no indentation if this tree item's level is not specified.
|
||||
level = level || 0;
|
||||
|
||||
// Don't increase indentation if this tree item is hidden.
|
||||
|
@ -66,6 +73,11 @@ function CallView({ autoExpandDepth, caller, frame, level, hidden, inverted }) {
|
|||
|
||||
AbstractTreeItem.call(this, { parent: caller, level });
|
||||
|
||||
this.sortingPredicate = sortingPredicate != null
|
||||
? sortingPredicate
|
||||
: caller ? caller.sortingPredicate
|
||||
: DEFAULT_SORTING_PREDICATE
|
||||
|
||||
this.autoExpandDepth = autoExpandDepth != null
|
||||
? autoExpandDepth
|
||||
: caller ? caller.autoExpandDepth
|
||||
|
@ -95,18 +107,23 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
|
|||
|
||||
let selfPercentage;
|
||||
let selfDuration;
|
||||
let totalAllocations;
|
||||
|
||||
if (!this._getChildCalls().length) {
|
||||
selfPercentage = framePercentage;
|
||||
selfDuration = this.frame.duration;
|
||||
totalAllocations = this.frame.allocations;
|
||||
} else {
|
||||
let childrenPercentage = sum(
|
||||
[this._getPercentage(c.samples) for (c of this._getChildCalls())]);
|
||||
let childrenDuration = sum(
|
||||
[c.duration for (c of this._getChildCalls())]);
|
||||
let childrenAllocations = sum(
|
||||
[c.allocations for (c of this._getChildCalls())]);
|
||||
|
||||
selfPercentage = clamp(framePercentage - childrenPercentage, 0, 100);
|
||||
selfDuration = this.frame.duration - childrenDuration;
|
||||
totalAllocations = this.frame.allocations + childrenAllocations;
|
||||
|
||||
if (this.inverted) {
|
||||
selfPercentage = framePercentage - selfPercentage;
|
||||
|
@ -118,6 +135,8 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
|
|||
let selfDurationCell = this._createTimeCell(selfDuration, true);
|
||||
let percentageCell = this._createExecutionCell(framePercentage);
|
||||
let selfPercentageCell = this._createExecutionCell(selfPercentage, true);
|
||||
let allocationsCell = this._createAllocationsCell(totalAllocations);
|
||||
let selfAllocationsCell = this._createAllocationsCell(this.frame.allocations, true);
|
||||
let samplesCell = this._createSamplesCell(this.frame.samples);
|
||||
let functionCell = this._createFunctionCell(arrowNode, frameInfo, this.level);
|
||||
|
||||
|
@ -138,8 +157,10 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
|
|||
|
||||
targetNode.appendChild(durationCell);
|
||||
targetNode.appendChild(percentageCell);
|
||||
targetNode.appendChild(allocationsCell);
|
||||
targetNode.appendChild(selfDurationCell);
|
||||
targetNode.appendChild(selfPercentageCell);
|
||||
targetNode.appendChild(selfAllocationsCell);
|
||||
targetNode.appendChild(samplesCell);
|
||||
targetNode.appendChild(functionCell);
|
||||
|
||||
|
@ -177,8 +198,9 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
|
|||
}));
|
||||
}
|
||||
|
||||
// Sort the "callees" asc. by samples, before inserting them in the tree.
|
||||
children.sort((a, b) => a.frame.samples < b.frame.samples ? 1 : -1);
|
||||
// Sort the "callees" asc. by samples, before inserting them in the tree,
|
||||
// if no other sorting predicate was specified on this on the root item.
|
||||
children.sort(this.sortingPredicate);
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -190,7 +212,7 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
|
|||
cell.className = "plain call-tree-cell";
|
||||
cell.setAttribute("type", isSelf ? "self-duration" : "duration");
|
||||
cell.setAttribute("crop", "end");
|
||||
cell.setAttribute("value", L10N.numberWithDecimals(duration, 2));
|
||||
cell.setAttribute("value", L10N.numberWithDecimals(duration, 2) + " " + MILLISECOND_UNITS);
|
||||
return cell;
|
||||
},
|
||||
_createExecutionCell: function(percentage, isSelf = false) {
|
||||
|
@ -198,7 +220,15 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
|
|||
cell.className = "plain call-tree-cell";
|
||||
cell.setAttribute("type", isSelf ? "self-percentage" : "percentage");
|
||||
cell.setAttribute("crop", "end");
|
||||
cell.setAttribute("value", L10N.numberWithDecimals(percentage, 2) + "%");
|
||||
cell.setAttribute("value", L10N.numberWithDecimals(percentage, 2) + PERCENTAGE_UNITS);
|
||||
return cell;
|
||||
},
|
||||
_createAllocationsCell: function(count, isSelf = false) {
|
||||
let cell = this.document.createElement("label");
|
||||
cell.className = "plain call-tree-cell";
|
||||
cell.setAttribute("type", isSelf ? "self-allocations" : "allocations");
|
||||
cell.setAttribute("crop", "end");
|
||||
cell.setAttribute("value", count || 0);
|
||||
return cell;
|
||||
},
|
||||
_createSamplesCell: function(count) {
|
||||
|
@ -271,6 +301,18 @@ CallView.prototype = Heritage.extend(AbstractTreeItem.prototype, {
|
|||
return cell;
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the allocations information hidden or visible.
|
||||
* @param boolean visible
|
||||
*/
|
||||
toggleAllocations: function(visible) {
|
||||
if (!visible) {
|
||||
this.container.setAttribute("allocations-hidden", "");
|
||||
} else {
|
||||
this.container.removeAttribute("allocations-hidden");
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Toggles the category information hidden or visible.
|
||||
* @param boolean visible
|
||||
|
|
|
@ -41,13 +41,23 @@
|
|||
- on a button that remvoes all the recordings. -->
|
||||
<!ENTITY profilerUI.clearButton "Clear">
|
||||
|
||||
<!-- LOCALIZATION NOTE (profilerUI.toolbar.*): These strings are displayed
|
||||
- in the toolbar on buttons that select which view is currently shown. -->
|
||||
<!ENTITY profilerUI.toolbar.waterfall "Timeline">
|
||||
<!ENTITY profilerUI.toolbar.js-calltree "JavaScript">
|
||||
<!ENTITY profilerUI.toolbar.memory-calltree "Memory">
|
||||
<!ENTITY profilerUI.toolbar.js-flamegraph "JS Flame Chart">
|
||||
<!ENTITY profilerUI.toolbar.memory-flamegraph "Memory Flame Chart">
|
||||
|
||||
<!-- LOCALIZATION NOTE (profilerUI.table.*): These strings are displayed
|
||||
- in the call tree headers for a recording. -->
|
||||
<!ENTITY profilerUI.table.totalDuration "Total Time (ms)">
|
||||
<!ENTITY profilerUI.table.selfDuration "Self Time (ms)">
|
||||
<!ENTITY profilerUI.table.totalDuration2 "Total Time">
|
||||
<!ENTITY profilerUI.table.selfDuration2 "Self Time">
|
||||
<!ENTITY profilerUI.table.totalPercentage "Total Cost">
|
||||
<!ENTITY profilerUI.table.selfPercentage "Self Cost">
|
||||
<!ENTITY profilerUI.table.samples "Samples">
|
||||
<!ENTITY profilerUI.table.totalAlloc "Total Allocations">
|
||||
<!ENTITY profilerUI.table.selfAlloc "Self Allocations">
|
||||
<!ENTITY profilerUI.table.function "Function">
|
||||
|
||||
<!-- LOCALIZATION NOTE (profilerUI.newtab.tooltiptext): The tooltiptext shown
|
||||
|
|
|
@ -83,6 +83,14 @@ category.graphics=Graphics
|
|||
category.storage=Storage
|
||||
category.events=Input & Events
|
||||
|
||||
# LOCALIZATION NOTE (graphs.ms):
|
||||
# This string is displayed in the call tree after units of time in milliseconds.
|
||||
table.ms=ms
|
||||
|
||||
# LOCALIZATION NOTE (graphs.ms):
|
||||
# This string is displayed in the call tree after units representing percentages.
|
||||
table.percentage=%
|
||||
|
||||
# LOCALIZATION NOTE (table.root):
|
||||
# This string is displayed in the call tree for the root node.
|
||||
table.root=(root)
|
||||
|
|
|
@ -28,9 +28,9 @@
|
|||
</g>
|
||||
<g id="details-call-tree">
|
||||
<rect x="0px" y="3px" width="16px" height="2px" rx="1" ry="1"/>
|
||||
<rect x="3px" y="6px" width="7px" height="2px" rx="1" ry="1"/>
|
||||
<rect x="6px" y="9px" width="6px" height="2px" rx="1" ry="1"/>
|
||||
<rect x="9px" y="12px" width="5px" height="2px" rx="1" ry="1"/>
|
||||
<rect x="0px" y="6px" width="8px" height="2px" rx="1" ry="1"/>
|
||||
<rect x="0px" y="9px" width="11px" height="2px" rx="1" ry="1"/>
|
||||
<rect x="0px" y="12px" width="6px" height="2px" rx="1" ry="1"/>
|
||||
</g>
|
||||
<g id="details-flamegraph">
|
||||
<rect x="0px" y="3px" width="16px" height="2px" rx="1" ry="1"/>
|
||||
|
|
До Ширина: | Высота: | Размер: 1.8 KiB После Ширина: | Высота: | Размер: 1.8 KiB |
|
@ -25,7 +25,14 @@
|
|||
-moz-border-end-color: var(--theme-splitter-color);
|
||||
}
|
||||
|
||||
/* Overview Panel */
|
||||
#performance-toolbar-controls-detail-views > toolbarbutton {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#performance-toolbar-controls-detail-views .toolbarbutton-text {
|
||||
-moz-padding-start: 4px;
|
||||
-moz-padding-end: 8px;
|
||||
}
|
||||
|
||||
#record-button {
|
||||
list-style-image: url(profiler-stopwatch.svg);
|
||||
|
@ -45,11 +52,13 @@
|
|||
list-style-image: url(performance-icons.svg#details-waterfall);
|
||||
}
|
||||
|
||||
#select-calltree-view {
|
||||
#select-js-calltree-view,
|
||||
#select-memory-calltree-view {
|
||||
list-style-image: url(performance-icons.svg#details-call-tree);
|
||||
}
|
||||
|
||||
#select-flamegraph-view {
|
||||
#select-js-flamegraph-view,
|
||||
#select-memory-flamegraph-view {
|
||||
list-style-image: url(performance-icons.svg#details-flamegraph);
|
||||
}
|
||||
|
||||
|
@ -61,27 +70,42 @@
|
|||
overflow: auto;
|
||||
}
|
||||
|
||||
.call-tree-cells-container[allocations-hidden] .call-tree-cell[type="allocations"],
|
||||
.call-tree-cells-container[allocations-hidden] .call-tree-cell[type="self-allocations"],
|
||||
.call-tree-cells-container[categories-hidden] .call-tree-category {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.call-tree-header {
|
||||
font-size: 90%;
|
||||
padding-top: 2px !important;
|
||||
padding-bottom: 2px !important;
|
||||
}
|
||||
|
||||
.call-tree-header[type="duration"],
|
||||
.call-tree-cell[type="duration"],
|
||||
.call-tree-header[type="self-duration"],
|
||||
.call-tree-cell[type="self-duration"] {
|
||||
width: 9em;
|
||||
width: 6vw;
|
||||
}
|
||||
|
||||
.call-tree-header[type="percentage"],
|
||||
.call-tree-cell[type="percentage"],
|
||||
.call-tree-header[type="self-percentage"],
|
||||
.call-tree-cell[type="self-percentage"] {
|
||||
width: 6em;
|
||||
width: 5vw;
|
||||
}
|
||||
|
||||
.call-tree-header[type="samples"],
|
||||
.call-tree-cell[type="samples"] {
|
||||
width: 5em;
|
||||
width: 4vw;
|
||||
}
|
||||
|
||||
.call-tree-header[type="allocations"],
|
||||
.call-tree-cell[type="allocations"],
|
||||
.call-tree-header[type="self-allocations"],
|
||||
.call-tree-cell[type="self-allocations"] {
|
||||
width: 7vw;
|
||||
}
|
||||
|
||||
.call-tree-header[type="function"],
|
||||
|
|
|
@ -220,6 +220,8 @@
|
|||
overflow: auto;
|
||||
}
|
||||
|
||||
.call-tree-cells-container[allocations-hidden] .call-tree-cell[type="allocations"],
|
||||
.call-tree-cells-container[allocations-hidden] .call-tree-cell[type="self-allocations"],
|
||||
.call-tree-cells-container[categories-hidden] .call-tree-category {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -165,11 +165,15 @@ let MemoryActor = protocol.ActorClass({
|
|||
? options.probability
|
||||
: 1.0;
|
||||
this.dbg.memory.trackingAllocationSites = true;
|
||||
|
||||
return Date.now();
|
||||
}), {
|
||||
request: {
|
||||
options: Arg(0, "nullable:AllocationsRecordingOptions")
|
||||
},
|
||||
response: {}
|
||||
response: {
|
||||
value: RetVal(0, "number")
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
|
@ -178,9 +182,13 @@ let MemoryActor = protocol.ActorClass({
|
|||
stopRecordingAllocations: method(expectState("attached", function() {
|
||||
this.dbg.memory.trackingAllocationSites = false;
|
||||
this._clearFrames();
|
||||
|
||||
return Date.now();
|
||||
}), {
|
||||
request: {},
|
||||
response: {}
|
||||
response: {
|
||||
value: RetVal(0, "number")
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
|
|
Загрузка…
Ссылка в новой задаче