diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js')
-rw-r--r-- | Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js | 262 |
1 files changed, 262 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js b/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js new file mode 100644 index 000000000..1691d9815 --- /dev/null +++ b/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js @@ -0,0 +1,262 @@ +/* + * 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. + */ + +WebInspector.MemoryTimelineOverviewGraph = class MemoryTimelineOverviewGraph extends WebInspector.TimelineOverviewGraph +{ + constructor(timeline, timelineOverview) + { + super(timelineOverview); + + this.element.classList.add("memory"); + + console.assert(timeline instanceof WebInspector.MemoryTimeline); + + this._memoryTimeline = timeline; + this._memoryTimeline.addEventListener(WebInspector.Timeline.Event.RecordAdded, this._memoryTimelineRecordAdded, this); + this._memoryTimeline.addEventListener(WebInspector.MemoryTimeline.Event.MemoryPressureEventAdded, this._memoryTimelineMemoryPressureEventAdded, this); + + this._didInitializeCategories = false; + + let size = new WebInspector.Size(0, this.height); + this._chart = new WebInspector.StackedLineChart(size); + 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(); + } + + // 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() + { + if (!this.visible) + 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 WebInspector.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]; + markerElement.style.left = 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); + + // Don't include the record before the graph start if the graph start is within a gap. + let includeRecordBeforeStart = !discontinuities.length || discontinuities[0].startTime > graphStartTime; + + // FIXME: <https://webkit.org/b/153759> Web Inspector: Memory Timelines should better extend to future data + let visibleRecords = this._memoryTimeline.recordsInTimeRange(graphStartTime, visibleEndTime, includeRecordBeforeStart); + 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, discontinuity, nextRecord) + { + console.assert(previousRecord || nextRecord); + if (!(previousRecord || nextRecord)) + return; + + let xStart = xScale(discontinuity.startTime); + let xEnd = xScale(discontinuity.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 discontinuity = discontinuities.shift(); + insertDiscontinuity.call(this, previousRecord, discontinuity, record); + } + + let x = xScale(record.startTime); + this._chart.addPointSet(x, pointSetForRecord(record)); + + previousRecord = record; + } + + if (discontinuities.length) + insertDiscontinuity.call(this, previousRecord, 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(visibleEndTime)); + this._chart.addPointSet(x, pointSetForRecord(lastRecord)); + } + } + + this._chart.updateLayout(); + } + + // 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 = WebInspector.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; + + 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); + } + + this.needsLayout(); + } + + _memoryTimelineMemoryPressureEventAdded(event) + { + this.needsLayout(); + } +}; |