⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 245498 in webkit


Ignore:
Timestamp:
May 17, 2019, 10:27:06 PM (7 years ago)
Author:
Devin Rousso
Message:

Web Inspector: Timelines: CPU/memory timeline bars sometimes don't draw correctly and jump around on scrolling
https://bugs.webkit.org/show_bug.cgi?id=197440
<rdar://problem/46886315>

Reviewed by Joseph Pecoraro.

When drawing the Memory/CPU graphs, we need to know about the record immediately before/after
what's overlapping the visible range so that the graph correctly slopes off the screen.

  • UserInterface/Models/Timeline.js:

(WI.Timeline.prototype.recordsInTimeRange):
(WI.Timeline.prototype.recordsOverlappingTimeRange): Deleted.
Merge recordsOverlappingTimeRange into recordsInTimeRange by accepting an options object
that determines whether to include the record before/after the first/last record that are
at all overlapping the range.

  • UserInterface/Models/CPUTimelineRecord.js:

(WI.CPUTimelineRecord):
(WI.CPUTimelineRecord.get samplingRatePerSecond): Added.
(WI.CPUTimelineRecord.prototype.adjustStartTimeToLastRecord): Added.

  • UserInterface/Models/MemoryTimelineRecord.js:

(WI.MemoryTimelineRecord):
(WI.MemoryTimelineRecord.get samplingRatePerSecond): Added.
(WI.MemoryTimelineRecord.prototype.adjustStartTimeToLastRecord): Added.
Adjust the startTime of the record by the sampling rate (which is 500ms).

  • UserInterface/Models/CPUTimeline.js: Added.

(WI.CPUTimeline.prototype.addRecord):

  • UserInterface/Models/MemoryTimeline.js:

(WI.MemoryTimeline.prototype.addRecord): Added.
Adjust the startTime of the new record to be equal to the endTime of the last record.

  • UserInterface/Views/TimelineOverview.js:

(WI.TimelineOverview.prototype._recordSelected):

  • UserInterface/Views/CPUTimelineView.js:

(WI.CPUTimelineView.prototype.layout):
(WI.CPUTimelineView.prototype._computeStatisticsData):
(WI.CPUTimelineView.prototype._attemptSelectIndicatatorTimelineRecord):

  • UserInterface/Views/CPUTimelineOverviewGraph.js:

(WI.CPUTimelineOverviewGraph.prototype.layout):
(WI.CPUTimelineOverviewGraph.prototype._handleChartClick):
(WI.CPUTimelineOverviewGraph.prototype.get samplingRatePerSecond): Deleted.
(WI.CPUTimelineOverviewGraph.prototype.layout.yScaleForRecord): Deleted.

  • UserInterface/Views/MemoryTimelineView.js:

(WI.MemoryTimelineView.prototype.layout):

  • UserInterface/Views/MemoryTimelineOverviewGraph.js:

(WI.MemoryTimelineOverviewGraph.prototype.layout):

  • UserInterface/Main.html:
  • UserInterface/Test.html:
Location:
trunk/Source/WebInspectorUI
Files:
12 edited
1 copied

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebInspectorUI/ChangeLog

    r245497 r245498  
     12019-05-17  Devin Rousso  <drousso@apple.com>
     2
     3        Web Inspector: Timelines: CPU/memory timeline bars sometimes don't draw correctly and jump around on scrolling
     4        https://bugs.webkit.org/show_bug.cgi?id=197440
     5        <rdar://problem/46886315>
     6
     7        Reviewed by Joseph Pecoraro.
     8
     9        When drawing the Memory/CPU graphs, we need to know about the record immediately before/after
     10        what's overlapping the visible range so that the graph correctly slopes off the screen.
     11
     12        * UserInterface/Models/Timeline.js:
     13        (WI.Timeline.prototype.recordsInTimeRange):
     14        (WI.Timeline.prototype.recordsOverlappingTimeRange): Deleted.
     15        Merge `recordsOverlappingTimeRange` into `recordsInTimeRange` by accepting an options object
     16        that determines whether to include the record before/after the first/last record that are
     17        at all overlapping the range.
     18
     19        * UserInterface/Models/CPUTimelineRecord.js:
     20        (WI.CPUTimelineRecord):
     21        (WI.CPUTimelineRecord.get samplingRatePerSecond): Added.
     22        (WI.CPUTimelineRecord.prototype.adjustStartTimeToLastRecord): Added.
     23        * UserInterface/Models/MemoryTimelineRecord.js:
     24        (WI.MemoryTimelineRecord):
     25        (WI.MemoryTimelineRecord.get samplingRatePerSecond): Added.
     26        (WI.MemoryTimelineRecord.prototype.adjustStartTimeToLastRecord): Added.
     27        Adjust the `startTime` of the record by the sampling rate (which is 500ms).
     28
     29        * UserInterface/Models/CPUTimeline.js: Added.
     30        (WI.CPUTimeline.prototype.addRecord):
     31        * UserInterface/Models/MemoryTimeline.js:
     32        (WI.MemoryTimeline.prototype.addRecord): Added.
     33        Adjust the `startTime` of the new record to be equal to the `endTime` of the last record.
     34
     35        * UserInterface/Views/TimelineOverview.js:
     36        (WI.TimelineOverview.prototype._recordSelected):
     37        * UserInterface/Views/CPUTimelineView.js:
     38        (WI.CPUTimelineView.prototype.layout):
     39        (WI.CPUTimelineView.prototype._computeStatisticsData):
     40        (WI.CPUTimelineView.prototype._attemptSelectIndicatatorTimelineRecord):
     41        * UserInterface/Views/CPUTimelineOverviewGraph.js:
     42        (WI.CPUTimelineOverviewGraph.prototype.layout):
     43        (WI.CPUTimelineOverviewGraph.prototype._handleChartClick):
     44        (WI.CPUTimelineOverviewGraph.prototype.get samplingRatePerSecond): Deleted.
     45        (WI.CPUTimelineOverviewGraph.prototype.layout.yScaleForRecord): Deleted.
     46
     47        * UserInterface/Views/MemoryTimelineView.js:
     48        (WI.MemoryTimelineView.prototype.layout):
     49        * UserInterface/Views/MemoryTimelineOverviewGraph.js:
     50        (WI.MemoryTimelineOverviewGraph.prototype.layout):
     51
     52        * UserInterface/Main.html:
     53        * UserInterface/Test.html:
     54
    1552019-05-17  Devin Rousso  <drousso@apple.com>
    256
  • trunk/Source/WebInspectorUI/UserInterface/Main.html

    r244351 r245498  
    357357    <script src="Models/Breakpoint.js"></script>
    358358    <script src="Models/CPUInstrument.js"></script>
     359    <script src="Models/CPUTimeline.js"></script>
    359360    <script src="Models/CPUTimelineRecord.js"></script>
    360361    <script src="Models/CSSCompletions.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimeline.js

    r245497 r245498  
    11/*
    2  * Copyright (C) 2016 Apple Inc. All rights reserved.
     2 * Copyright (C) 2019 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2424 */
    2525
    26 WI.MemoryTimeline = class MemoryTimeline extends WI.Timeline
     26WI.CPUTimeline = class CPUTimeline extends WI.Timeline
    2727{
    2828    // Public
    2929
    30     get memoryPressureEvents() { return this._pressureEvents; }
     30    addRecord(record)
     31    {
     32        let lastRecord = this.records.lastValue;
     33        if (lastRecord)
     34            record.adjustStartTimeToLastRecord(lastRecord);
    3135
    32     addMemoryPressureEvent(memoryPressureEvent)
    33     {
    34         console.assert(memoryPressureEvent instanceof WI.MemoryPressureEvent);
    35 
    36         this._pressureEvents.push(memoryPressureEvent);
    37 
    38         this.dispatchEventToListeners(WI.MemoryTimeline.Event.MemoryPressureEventAdded, {memoryPressureEvent});
    39     }
    40 
    41     // Protected
    42 
    43     reset(suppressEvents)
    44     {
    45         super.reset(suppressEvents);
    46 
    47         this._pressureEvents = [];
     36        super.addRecord(record);
    4837    }
    4938};
    50 
    51 WI.MemoryTimeline.Event = {
    52     MemoryPressureEventAdded: "memory-timeline-memory-pressure-event-added",
    53 };
  • trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimelineRecord.js

    r243024 r245498  
    2828    constructor({timestamp, usage, threads})
    2929    {
    30         super(WI.TimelineRecord.Type.CPU, timestamp, timestamp);
     30        super(WI.TimelineRecord.Type.CPU, timestamp - CPUTimelineRecord.samplingRatePerSecond, timestamp);
    3131
    3232        console.assert(typeof timestamp === "number");
     
    6969    }
    7070
     71    // Static
     72
     73    static get samplingRatePerSecond()
     74    {
     75        // 500ms. This matches the ResourceUsageThread sampling frequency in the backend.
     76        return 0.5;
     77    }
     78
    7179    // Import / Export
    7280
     
    96104    get unknownThreadUsage() { return this._unknownThreadUsage; }
    97105    get workersData() { return this._workersData; }
     106
     107    adjustStartTimeToLastRecord(lastRecord)
     108    {
     109        console.assert(lastRecord instanceof CPUTimelineRecord);
     110        console.assert(this._startTime >= lastRecord.endTime);
     111        this._startTime = lastRecord.endTime;
     112    }
    98113};
  • trunk/Source/WebInspectorUI/UserInterface/Models/MemoryTimeline.js

    r220119 r245498  
    4747        this._pressureEvents = [];
    4848    }
     49
     50    addRecord(record)
     51    {
     52        let lastRecord = this.records.lastValue;
     53        if (lastRecord)
     54            record.adjustStartTimeToLastRecord(lastRecord);
     55
     56        super.addRecord(record);
     57    }
    4958};
    5059
  • trunk/Source/WebInspectorUI/UserInterface/Models/MemoryTimelineRecord.js

    r243024 r245498  
    2828    constructor(timestamp, categories)
    2929    {
    30         super(WI.TimelineRecord.Type.Memory, timestamp, timestamp);
     30        super(WI.TimelineRecord.Type.Memory, timestamp - MemoryTimelineRecord.samplingRatePerSecond, timestamp);
    3131
    3232        console.assert(typeof timestamp === "number");
     
    4343
    4444    // Static
     45
     46    static get samplingRatePerSecond()
     47    {
     48        // 500ms. This matches the ResourceUsageThread sampling frequency in the backend.
     49        return 0.5;
     50    }
    4551
    4652    static memoryCategoriesFromProtocol(categories)
     
    103109    get categories() { return this._categories; }
    104110    get totalSize() { return this._totalSize; }
     111
     112    adjustStartTimeToLastRecord(lastRecord)
     113    {
     114        console.assert(lastRecord instanceof MemoryTimelineRecord);
     115        console.assert(this._startTime >= lastRecord.endTime);
     116        this._startTime = lastRecord.endTime;
     117    }
    105118};
  • trunk/Source/WebInspectorUI/UserInterface/Models/Timeline.js

    r242567 r245498  
    4141        if (type === WI.TimelineRecord.Type.Network)
    4242            return new WI.NetworkTimeline(type);
     43
     44        if (type === WI.TimelineRecord.Type.CPU)
     45            return new WI.CPUTimeline(type);
    4346
    4447        if (type === WI.TimelineRecord.Type.Memory)
     
    112115    }
    113116
    114     recordsOverlappingTimeRange(startTime, endTime)
     117    recordsInTimeRange(startTime, endTime, {includeRecordBeforeStart, includeRecordAfterEnd} = {})
    115118    {
    116119        let lowerIndex = this._records.lowerBound(startTime, (time, record) => time - record.endTime);
    117         let upperIndex = this._records.upperBound(endTime, (time, record) => time - record.startTime);
    118 
    119         return this._records.slice(lowerIndex, upperIndex);
    120     }
    121 
    122     recordsInTimeRange(startTime, endTime, includeRecordBeforeStart)
    123     {
    124         let lowerIndex = this._records.lowerBound(startTime, (time, record) => time - record.startTime);
    125         let upperIndex = this._records.upperBound(endTime, (time, record) => time - record.startTime);
    126 
    127         // Include the record right before the start time.
    128120        if (includeRecordBeforeStart && lowerIndex > 0) {
    129121            lowerIndex--;
     
    137129            }
    138130        }
     131
     132        let upperIndex = this._records.upperBound(endTime, (time, record) => time - record.startTime);
     133        if (includeRecordAfterEnd && upperIndex < this._records.length)
     134            ++upperIndex;
    139135
    140136        return this._records.slice(lowerIndex, upperIndex);
  • trunk/Source/WebInspectorUI/UserInterface/Test.html

    r244154 r245498  
    119119    <script src="Models/Breakpoint.js"></script>
    120120    <script src="Models/CPUInstrument.js"></script>
     121    <script src="Models/CPUTimeline.js"></script>
    121122    <script src="Models/CPUTimelineRecord.js"></script>
    122123    <script src="Models/CSSCompletions.js"></script>
  • trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.js

    r243166 r245498  
    5757    }
    5858
    59     // Static
    60 
    61     static get samplingRatePerSecond()
    62     {
    63         // 500ms. This matches the ResourceUsageThread sampling frequency in the backend.
    64         return 0.5;
    65     }
    66 
    6759    // Protected
    6860
     
    116108        }
    117109
    118         const includeRecordBeforeStart = true;
    119         let visibleRecords = this._cpuTimeline.recordsInTimeRange(graphStartTime, visibleEndTime, includeRecordBeforeStart);
     110        let visibleRecords = this._cpuTimeline.recordsInTimeRange(graphStartTime, visibleEndTime, {
     111            includeRecordBeforeStart: true,
     112        });
    120113        if (!visibleRecords.length)
    121114            return;
    122115
    123         function yScaleForRecord(record) {
    124             return yScale(record.usage);
    125         }
    126 
    127         let intervalWidth = CPUTimelineOverviewGraph.samplingRatePerSecond / secondsPerPixel;
    128116        const minimumDisplayHeight = 4;
    129117
    130118        for (let record of visibleRecords) {
    131119            let additionalClass = record === this.selectedRecord ? "selected" : undefined;
    132             let w = intervalWidth;
    133             let x = xScale(record.startTime - CPUTimelineOverviewGraph.samplingRatePerSecond);
     120            let w = (record.endTime - record.startTime) / secondsPerPixel;
     121            let x = xScale(record.startTime);
    134122            let h1 = Math.max(minimumDisplayHeight, yScale(record.mainThreadUsage));
    135123            let h2 = Math.max(minimumDisplayHeight, yScale(record.mainThreadUsage + record.workerThreadUsage));
     
    200188
    201189        let clickTime = graphStartTime + graphClickTime;
    202         let record = this._cpuTimeline.closestRecordTo(clickTime + (CPUTimelineOverviewGraph.samplingRatePerSecond / 2));
     190        let record = this._cpuTimeline.closestRecordTo(clickTime);
    203191        if (!record)
    204192            return;
  • trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js

    r245060 r245498  
    446446        let originalDiscontinuities = discontinuities.slice();
    447447
    448         // Don't include the record before the graph start if the graph start is within a gap.
    449         let includeRecordBeforeStart = !discontinuities.length || discontinuities[0].startTime > graphStartTime;
    450         let visibleRecords = this.representedObject.recordsInTimeRange(graphStartTime, visibleEndTime, includeRecordBeforeStart);
     448        let visibleRecords = this.representedObject.recordsInTimeRange(graphStartTime, visibleEndTime, {
     449            includeRecordBeforeStart: !discontinuities.length || discontinuities[0].startTime > graphStartTime,
     450            includeRecordAfterEnd: true,
     451        });
    451452        if (!visibleRecords.length || (visibleRecords.length === 1 && visibleRecords[0].endTime < graphStartTime)) {
    452453            this.clear();
     
    11621163        // Main Thread activity.
    11631164
    1164         const includeRecordBeforeStart = true;
    1165 
    11661165        function incrementTypeCount(map, key) {
    11671166            let entry = map.get(key);
     
    11841183
    11851184        let scriptTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Script);
    1186         let scriptRecords = scriptTimeline ? scriptTimeline.recordsInTimeRange(startTime, endTime, includeRecordBeforeStart) : [];
     1185        let scriptRecords = scriptTimeline ? scriptTimeline.recordsInTimeRange(startTime, endTime, {includeRecordBeforeStart: true}) : [];
    11871186        scriptRecords = scriptRecords.filter((record) => {
    11881187            // Return true for event types that define script entries/exits.
     
    12501249
    12511250        let layoutTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Layout);
    1252         let layoutRecords = layoutTimeline ? layoutTimeline.recordsInTimeRange(startTime, endTime, includeRecordBeforeStart) : [];
     1251        let layoutRecords = layoutTimeline ? layoutTimeline.recordsInTimeRange(startTime, endTime, {includeRecordBeforeStart: true}) : [];
    12531252        layoutRecords = layoutRecords.filter((record) => {
    12541253            switch (record.eventType) {
     
    15741573    {
    15751574        let layoutTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Layout);
    1576         let layoutRecords = layoutTimeline ? layoutTimeline.recordsOverlappingTimeRange(startTime, endTime) : [];
     1575        let layoutRecords = layoutTimeline ? layoutTimeline.recordsInTimeRange(startTime, endTime, {includeRecordBeforeStart: true}) : [];
    15771576        layoutRecords = layoutRecords.filter((record) => {
    15781577            switch (record.eventType) {
     
    15981597
    15991598        let scriptTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Script);
    1600         let scriptRecords = scriptTimeline ? scriptTimeline.recordsOverlappingTimeRange(startTime, endTime) : [];
     1599        let scriptRecords = scriptTimeline ? scriptTimeline.recordsInTimeRange(startTime, endTime, {includeRecordBeforeStart: true}) : [];
    16011600        scriptRecords = scriptRecords.filter((record) => {
    16021601            switch (record.eventType) {
  • trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js

    r243024 r245498  
    139139        let discontinuities = this.timelineOverview.discontinuitiesInTimeRange(graphStartTime, visibleEndTime);
    140140
    141         // Don't include the record before the graph start if the graph start is within a gap.
    142         let includeRecordBeforeStart = !discontinuities.length || discontinuities[0].startTime > graphStartTime;
    143 
    144         // FIXME: <https://webkit.org/b/153759> Web Inspector: Memory Timelines should better extend to future data
    145         let visibleRecords = this._memoryTimeline.recordsInTimeRange(graphStartTime, visibleEndTime, includeRecordBeforeStart);
     141        let visibleRecords = this._memoryTimeline.recordsInTimeRange(graphStartTime, visibleEndTime, {
     142            includeRecordBeforeStart: !discontinuities.length || discontinuities[0].startTime > graphStartTime,
     143            includeRecordAfterEnd: true,
     144        });
    146145        if (!visibleRecords.length)
    147146            return;
  • trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineView.js

    r243024 r245498  
    198198        let discontinuities = this._recording.discontinuitiesInTimeRange(graphStartTime, visibleEndTime);
    199199
    200         // Don't include the record before the graph start if the graph start is within a gap.
    201         let includeRecordBeforeStart = !discontinuities.length || discontinuities[0].startTime > graphStartTime;
    202 
    203         // FIXME: <https://webkit.org/b/153759> Web Inspector: Memory Timelines should better extend to future data
    204         let visibleRecords = this.representedObject.recordsInTimeRange(graphStartTime, visibleEndTime, includeRecordBeforeStart);
     200        let visibleRecords = this.representedObject.recordsInTimeRange(graphStartTime, visibleEndTime, {
     201            includeRecordBeforeStart: !discontinuities.length || discontinuities[0].startTime > graphStartTime,
     202            includeRecordAfterEnd: true,
     203        });
    205204        if (!visibleRecords.length || (visibleRecords.length === 1 && visibleRecords[0].endTime < graphStartTime)) {
    206205            this.clear();
  • trunk/Source/WebInspectorUI/UserInterface/Views/TimelineOverview.js

    r244195 r245498  
    785785            let startTime = firstRecord instanceof WI.RenderingFrameTimelineRecord ? firstRecord.frameIndex : firstRecord.startTime;
    786786            let endTime = lastRecord instanceof WI.RenderingFrameTimelineRecord ? lastRecord.frameIndex : lastRecord.endTime;
    787 
    788             if (firstRecord instanceof WI.CPUTimelineRecord) {
    789                 let selectionPadding = WI.CPUTimelineOverviewGraph.samplingRatePerSecond * 2.25;
    790                 this.selectionStartTime = startTime - selectionPadding - (WI.CPUTimelineOverviewGraph.samplingRatePerSecond / 2);
    791                 this.selectionDuration = endTime - startTime + (selectionPadding * 2);
    792             } else if (startTime < this.selectionStartTime || endTime > this.selectionStartTime + this.selectionDuration) {
     787            if (startTime < this.selectionStartTime || (endTime > this.selectionStartTime + this.selectionDuration) || firstRecord instanceof WI.CPUTimelineRecord) {
    793788                let selectionPadding = this.secondsPerPixel * 10;
    794789                this.selectionStartTime = startTime - selectionPadding;
Note: See TracChangeset for help on using the changeset viewer.