moved to root
This commit is contained in:
@@ -0,0 +1,275 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Apple Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
|
||||
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
|
||||
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
||||
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
|
||||
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
|
||||
* THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
WI.MemoryTimelineOverviewGraph = class MemoryTimelineOverviewGraph extends WI.TimelineOverviewGraph
|
||||
{
|
||||
constructor(timeline, timelineOverview)
|
||||
{
|
||||
console.assert(timeline instanceof WI.MemoryTimeline);
|
||||
|
||||
super(timelineOverview);
|
||||
|
||||
this.element.classList.add("memory");
|
||||
|
||||
this._memoryTimeline = timeline;
|
||||
this._memoryTimeline.addEventListener(WI.Timeline.Event.RecordAdded, this._memoryTimelineRecordAdded, this);
|
||||
this._memoryTimeline.addEventListener(WI.MemoryTimeline.Event.MemoryPressureEventAdded, this._memoryTimelineMemoryPressureEventAdded, this);
|
||||
|
||||
this._didInitializeCategories = false;
|
||||
|
||||
this._chart = new WI.StackedAreaChart;
|
||||
this._chart.size = new WI.Size(0, this.height);
|
||||
this.addSubview(this._chart);
|
||||
this.element.appendChild(this._chart.element);
|
||||
|
||||
this._legendElement = this.element.appendChild(document.createElement("div"));
|
||||
this._legendElement.classList.add("legend");
|
||||
|
||||
this._memoryPressureMarkersContainerElement = this.element.appendChild(document.createElement("div"));
|
||||
this._memoryPressureMarkersContainerElement.classList.add("memory-pressure-markers-container");
|
||||
this._memoryPressureMarkerElements = [];
|
||||
|
||||
this.reset();
|
||||
|
||||
for (let record of this._memoryTimeline.records)
|
||||
this._processRecord(record);
|
||||
}
|
||||
|
||||
// Protected
|
||||
|
||||
get height()
|
||||
{
|
||||
return 108;
|
||||
}
|
||||
|
||||
reset()
|
||||
{
|
||||
super.reset();
|
||||
|
||||
this._maxSize = 0;
|
||||
this._cachedMaxSize = undefined;
|
||||
|
||||
this._updateLegend();
|
||||
this._chart.clear();
|
||||
this._chart.needsLayout();
|
||||
|
||||
this._memoryPressureMarkersContainerElement.removeChildren();
|
||||
this._memoryPressureMarkerElements = [];
|
||||
}
|
||||
|
||||
layout()
|
||||
{
|
||||
super.layout();
|
||||
|
||||
if (this.hidden)
|
||||
return;
|
||||
|
||||
this._updateLegend();
|
||||
this._chart.clear();
|
||||
|
||||
if (!this._didInitializeCategories)
|
||||
return;
|
||||
|
||||
let graphWidth = this.timelineOverview.scrollContainerWidth;
|
||||
if (isNaN(graphWidth))
|
||||
return;
|
||||
|
||||
if (this._chart.size.width !== graphWidth || this._chart.size.height !== this.height)
|
||||
this._chart.size = new WI.Size(graphWidth, this.height);
|
||||
|
||||
let graphStartTime = this.startTime;
|
||||
let visibleEndTime = Math.min(this.endTime, this.currentTime);
|
||||
|
||||
let secondsPerPixel = this.timelineOverview.secondsPerPixel;
|
||||
let maxCapacity = this._maxSize * 1.05; // Add 5% for padding.
|
||||
|
||||
function xScale(time) {
|
||||
return (time - graphStartTime) / secondsPerPixel;
|
||||
}
|
||||
|
||||
let height = this.height;
|
||||
function yScale(size) {
|
||||
return height - ((size / maxCapacity) * height);
|
||||
}
|
||||
|
||||
let visibleMemoryPressureEventMarkers = this._visibleMemoryPressureEvents(graphStartTime, visibleEndTime);
|
||||
|
||||
// Reuse existing marker elements.
|
||||
for (let i = 0; i < visibleMemoryPressureEventMarkers.length; ++i) {
|
||||
let markerElement = this._memoryPressureMarkerElements[i];
|
||||
if (!markerElement) {
|
||||
markerElement = this._memoryPressureMarkersContainerElement.appendChild(document.createElement("div"));
|
||||
markerElement.classList.add("memory-pressure-event");
|
||||
this._memoryPressureMarkerElements[i] = markerElement;
|
||||
}
|
||||
|
||||
let memoryPressureEvent = visibleMemoryPressureEventMarkers[i];
|
||||
let property = WI.resolvedLayoutDirection() === WI.LayoutDirection.RTL ? "right" : "left";
|
||||
markerElement.style.setProperty(property, `${xScale(memoryPressureEvent.timestamp)}px`);
|
||||
}
|
||||
|
||||
// Remove excess marker elements.
|
||||
let excess = this._memoryPressureMarkerElements.length - visibleMemoryPressureEventMarkers.length;
|
||||
if (excess) {
|
||||
let elementsToRemove = this._memoryPressureMarkerElements.splice(visibleMemoryPressureEventMarkers.length);
|
||||
for (let element of elementsToRemove)
|
||||
element.remove();
|
||||
}
|
||||
|
||||
let discontinuities = this.timelineOverview.discontinuitiesInTimeRange(graphStartTime, visibleEndTime);
|
||||
|
||||
let visibleRecords = this._memoryTimeline.recordsInTimeRange(graphStartTime, visibleEndTime, {
|
||||
includeRecordBeforeStart: !discontinuities.length || discontinuities[0].startTime > graphStartTime,
|
||||
includeRecordAfterEnd: true,
|
||||
});
|
||||
if (!visibleRecords.length)
|
||||
return;
|
||||
|
||||
function pointSetForRecord(record) {
|
||||
let size = 0;
|
||||
let ys = [];
|
||||
for (let i = 0; i < record.categories.length; ++i) {
|
||||
size += record.categories[i].size;
|
||||
ys[i] = yScale(size);
|
||||
}
|
||||
return ys;
|
||||
}
|
||||
|
||||
// Extend the first record to the start so it doesn't look like we originate at zero size.
|
||||
if (visibleRecords[0] === this._memoryTimeline.records[0] && (!discontinuities.length || discontinuities[0].startTime > visibleRecords[0].startTime))
|
||||
this._chart.addPointSet(0, pointSetForRecord(visibleRecords[0]));
|
||||
|
||||
function insertDiscontinuity(previousRecord, startDiscontinuity, endDiscontinuity, nextRecord)
|
||||
{
|
||||
console.assert(previousRecord || nextRecord);
|
||||
if (!(previousRecord || nextRecord))
|
||||
return;
|
||||
|
||||
let xStart = xScale(previousRecord ? previousRecord.endTime : startDiscontinuity.startTime);
|
||||
let xEnd = xScale(endDiscontinuity.endTime);
|
||||
|
||||
// Extend the previous record to the start of the discontinuity.
|
||||
if (previousRecord)
|
||||
this._chart.addPointSet(xStart, pointSetForRecord(previousRecord));
|
||||
|
||||
let zeroValues = Array((previousRecord || nextRecord).categories.length).fill(yScale(0));
|
||||
this._chart.addPointSet(xStart, zeroValues);
|
||||
|
||||
if (nextRecord) {
|
||||
this._chart.addPointSet(xEnd, zeroValues);
|
||||
this._chart.addPointSet(xEnd, pointSetForRecord(nextRecord));
|
||||
} else {
|
||||
// Extend the discontinuity to the visible end time to prevent
|
||||
// drawing artifacts when the next record arrives.
|
||||
this._chart.addPointSet(xScale(visibleEndTime), zeroValues);
|
||||
}
|
||||
}
|
||||
|
||||
// Points for visible records.
|
||||
let previousRecord = null;
|
||||
for (let record of visibleRecords) {
|
||||
if (discontinuities.length && discontinuities[0].endTime <= record.startTime) {
|
||||
let startDiscontinuity = discontinuities.shift();
|
||||
let endDiscontinuity = startDiscontinuity;
|
||||
while (discontinuities.length && discontinuities[0].endTime <= record.startTime)
|
||||
endDiscontinuity = discontinuities.shift();
|
||||
insertDiscontinuity.call(this, previousRecord, startDiscontinuity, endDiscontinuity, record);
|
||||
}
|
||||
|
||||
let x = xScale(record.startTime);
|
||||
this._chart.addPointSet(x, pointSetForRecord(record));
|
||||
|
||||
previousRecord = record;
|
||||
}
|
||||
|
||||
if (discontinuities.length)
|
||||
insertDiscontinuity.call(this, previousRecord, discontinuities[0], discontinuities[0], null);
|
||||
else {
|
||||
// Extend the last value to current / end time.
|
||||
let lastRecord = visibleRecords.lastValue;
|
||||
if (lastRecord.startTime <= visibleEndTime) {
|
||||
let x = Math.floor(xScale(lastRecord.endTime));
|
||||
this._chart.addPointSet(x, pointSetForRecord(lastRecord));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Private
|
||||
|
||||
_updateLegend()
|
||||
{
|
||||
if (this._cachedMaxSize === this._maxSize)
|
||||
return;
|
||||
|
||||
this._cachedMaxSize = this._maxSize;
|
||||
|
||||
if (!this._maxSize) {
|
||||
this._legendElement.hidden = true;
|
||||
this._legendElement.textContent = "";
|
||||
} else {
|
||||
this._legendElement.hidden = false;
|
||||
this._legendElement.textContent = WI.UIString("Maximum Size: %s").format(Number.bytesToString(this._maxSize));
|
||||
}
|
||||
}
|
||||
|
||||
_visibleMemoryPressureEvents(startTime, endTime)
|
||||
{
|
||||
let events = this._memoryTimeline.memoryPressureEvents;
|
||||
if (!events.length)
|
||||
return [];
|
||||
|
||||
let lowerIndex = events.lowerBound(startTime, (time, event) => time - event.timestamp);
|
||||
let upperIndex = events.upperBound(endTime, (time, event) => time - event.timestamp);
|
||||
return events.slice(lowerIndex, upperIndex);
|
||||
}
|
||||
|
||||
_memoryTimelineRecordAdded(event)
|
||||
{
|
||||
let memoryTimelineRecord = event.data.record;
|
||||
console.assert(memoryTimelineRecord instanceof WI.MemoryTimelineRecord);
|
||||
|
||||
this._processRecord(memoryTimelineRecord);
|
||||
|
||||
this.needsLayout();
|
||||
}
|
||||
|
||||
_processRecord(memoryTimelineRecord)
|
||||
{
|
||||
this._maxSize = Math.max(this._maxSize, memoryTimelineRecord.totalSize);
|
||||
|
||||
if (!this._didInitializeCategories) {
|
||||
this._didInitializeCategories = true;
|
||||
let types = [];
|
||||
for (let category of memoryTimelineRecord.categories)
|
||||
types.push(category.type);
|
||||
this._chart.initializeSections(types);
|
||||
}
|
||||
}
|
||||
|
||||
_memoryTimelineMemoryPressureEventAdded(event)
|
||||
{
|
||||
this.needsLayout();
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user