Changeset 242073 in webkit
- Timestamp:
- Feb 25, 2019, 10:36:34 PM (6 years ago)
- Location:
- trunk/Source/WebInspectorUI
- Files:
-
- 1 added
- 17 edited
- 2 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/WebInspectorUI/ChangeLog
r242072 r242073 1 2019-02-25 Joseph Pecoraro <pecoraro@apple.com> 2 3 Web Inspector: CPU Usage Timeline - Thread Breakdown 4 https://bugs.webkit.org/show_bug.cgi?id=194788 5 6 Reviewed by Devin Rousso. 7 8 * Localizations/en.lproj/localizedStrings.js: 9 * UserInterface/Main.html: 10 New strings and files. 11 12 * UserInterface/Views/Variables.css: 13 (:root): 14 New colors for cpu threads / activity breakdown. 15 16 * UserInterface/Models/CPUTimelineRecord.js: 17 (WI.CPUTimelineRecord.prototype.get workers): 18 (WI.CPUTimelineRecord): 19 Distinguish the workers in a CPU timeline record. 20 21 * UserInterface/Views/CPUTimelineOverviewGraph.js: 22 (WI.CPUTimelineOverviewGraph): 23 (WI.CPUTimelineOverviewGraph.prototype.layout): 24 * UserInterface/Views/CPUTimelineOverviewGraph.css: 25 (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect): 26 (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.main-thread-usage): 27 (.timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.worker-thread-usage): 28 (.timeline-overview-graph.cpu > .column-chart > svg > rect): 29 Stacked column chart for CPU in the overview graph. 30 31 * UserInterface/Views/CPUTimelineView.css: 32 (.timeline-view.cpu > .content > .overview): 33 (.timeline-view.cpu > .content > .details > .subtitle.threads): 34 (.timeline-view.cpu > .content > .overview > .chart): 35 (.timeline-view.cpu > .content > .overview > .chart > .subtitle): 36 (.timeline-view.cpu > .content > .overview > .chart > .container): 37 (.timeline-view.cpu > .content > .overview .samples,): 38 (.timeline-view.cpu .legend): 39 (.timeline-view.cpu .legend .row): 40 (.timeline-view.cpu .legend .row + .row): 41 (.timeline-view.cpu .legend .swatch): 42 (.timeline-view.cpu .legend > .row > .swatch.sample-type-idle): 43 (.timeline-view.cpu .legend > .row > .swatch.sample-type-script): 44 (.timeline-view.cpu .legend > .row > .swatch.sample-type-style): 45 (.timeline-view.cpu .legend > .row > .swatch.sample-type-layout): 46 (.timeline-view.cpu .legend > .row > .swatch.sample-type-paint): 47 (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-idle): 48 (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-script): 49 (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-style): 50 (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-layout): 51 (.timeline-view.cpu .circle-chart > svg > path.segment.sample-type-paint): 52 (.timeline-view.cpu svg > path): 53 (.timeline-view.cpu .main-thread svg > path,): 54 (.timeline-view.cpu .worker-thread svg > path,): 55 (.timeline-view.cpu .cpu-usage-view.empty): 56 (.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers): 57 (.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div): 58 (.timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div > .label): 59 (.timeline-view.cpu > .content): Deleted. 60 (.cpu-usage-view .line-chart > svg > path): Deleted. 61 (.timeline-view.cpu .legend > .row > .swatch.current): Deleted. 62 * UserInterface/Views/CPUTimelineView.js: 63 (WI.CPUTimelineView): 64 (WI.CPUTimelineView.displayNameForSampleType): 65 (WI.CPUTimelineView.prototype.shown): 66 (WI.CPUTimelineView.prototype.clear.clearUsageView): 67 (WI.CPUTimelineView.prototype.clear): 68 (WI.CPUTimelineView.prototype.initialLayout.createChartContainer): 69 (WI.CPUTimelineView.prototype.initialLayout.appendLegendRow): 70 (WI.CPUTimelineView.prototype.initialLayout): 71 (WI.CPUTimelineView.prototype.layout.removeGreaterThan): 72 (WI.CPUTimelineView.prototype.layout): 73 (WI.CPUTimelineView.prototype.layout.layoutView): 74 (WI.CPUTimelineView.prototype.layout.yScale): 75 (WI.CPUTimelineView.prototype._computeSamplingData.markRecordEntries): 76 (WI.CPUTimelineView.prototype._computeSamplingData): 77 (WI.CPUTimelineView.prototype._removeWorkerThreadViews): 78 (WI.CPUTimelineView.prototype._clearBreakdownLegend): 79 (WI.CPUTimelineView.prototype.layout.xScale): Deleted. 80 Line charts and Circle Chart for threads and breakdowns. 81 82 * UserInterface/Views/CPUUsageStackedView.css: 83 (.cpu-usage-stacked-view): 84 (.cpu-usage-stacked-view > .details): 85 (body[dir=ltr] .cpu-usage-stacked-view > .details): 86 (body[dir=rtl] .cpu-usage-stacked-view > .details): 87 (.cpu-usage-stacked-view > .details > .name): 88 (body[dir=rtl] .cpu-usage-stacked-view > .graph): 89 (.cpu-usage-stacked-view > .graph): 90 (.cpu-usage-stacked-view > .graph,): 91 * UserInterface/Views/CPUUsageStackedView.js: 92 (WI.CPUUsageStackedView): 93 (WI.CPUUsageStackedView.prototype.get chart): 94 (WI.CPUUsageStackedView.prototype.clear): 95 (WI.CPUUsageStackedView.prototype.updateChart): 96 (WI.CPUUsageStackedView.prototype._updateDetails): 97 Same as CPUUsageView except Stacked for the total. 98 99 * UserInterface/Views/CPUUsageView.css: 100 (.cpu-usage-view): 101 (.cpu-usage-view > .details): 102 (.cpu-usage-view > .details > .name): 103 (.cpu-usage-view > .graph): 104 * UserInterface/Views/CPUUsageView.js: 105 (WI.CPUUsageView): 106 (WI.CPUUsageView.prototype.get chart): 107 (WI.CPUUsageView.prototype.clear): 108 (WI.CPUUsageView.prototype.updateChart): 109 (WI.CPUUsageView.prototype._updateDetails): 110 Slight modifications for the new UI. 111 112 * UserInterface/Views/LegacyCPUTimelineView.css: 113 (.timeline-view.legacy-cpu .cpu-usage-view .line-chart > svg > path): 114 * UserInterface/Views/LegacyCPUTimelineView.js: 115 (WI.LegacyCPUTimelineView.prototype.layout): 116 Update API calls in the legacy view for minor changes. 117 118 * UserInterface/Views/MemoryCategoryView.css: 119 (.memory-category-view > .details): 120 (.memory-category-view > .details > .name): 121 * UserInterface/Views/MemoryTimelineOverviewGraph.js: 122 (WI.MemoryTimelineOverviewGraph.prototype.layout): 123 * UserInterface/Views/MemoryTimelineView.css: 124 (body .timeline-view.memory): 125 (.timeline-view.memory): Deleted. 126 Improvements ported from the CPU timeline views. 127 128 * UserInterface/Views/StackedColumnChart.js: Added. 129 (WI.StackedColumnChart): 130 (WI.StackedColumnChart.prototype.get size): 131 (WI.StackedColumnChart.prototype.set size): 132 (WI.StackedColumnChart.prototype.initializeSections): 133 (WI.StackedColumnChart.prototype.addColumnSet): 134 (WI.StackedColumnChart.prototype.clear): 135 (WI.StackedColumnChart.prototype.layout): 136 A stacked column chart implementation. 137 138 * UserInterface/Views/View.js: 139 (WI.View.prototype.removeUnparentedSubview): 140 Add a way to remove a subview that had its `element` moved 141 someplace other than a direct child of our element. 142 1 143 2019-02-25 Joseph Pecoraro <pecoraro@apple.com> 2 144 -
trunk/Source/WebInspectorUI/Localizations/en.lproj/localizedStrings.js
r242049 r242073 159 159 localizedStrings["Breakdown"] = "Breakdown"; 160 160 localizedStrings["Breakdown of each memory category at the end of the selected time range"] = "Breakdown of each memory category at the end of the selected time range"; 161 localizedStrings["Breakdown of time spent on the main thread"] = "Breakdown of time spent on the main thread"; 161 162 localizedStrings["Breakpoint"] = "Breakpoint"; 162 163 localizedStrings["Breakpoints"] = "Breakpoints"; … … 600 601 localizedStrings["MIME Type:"] = "MIME Type:"; 601 602 localizedStrings["MSE Logging:"] = "MSE Logging:"; 603 localizedStrings["Main Thread"] = "Main Thread"; 602 604 localizedStrings["Manifest URL"] = "Manifest URL"; 603 605 localizedStrings["Mass"] = "Mass"; … … 689 691 localizedStrings["Other"] = "Other"; 690 692 localizedStrings["Other Issue"] = "Other Issue"; 693 localizedStrings["Other Threads"] = "Other Threads"; 691 694 localizedStrings["Other\u2026"] = "Other\u2026"; 692 695 localizedStrings["Outgoing message"] = "Outgoing message"; … … 956 959 localizedStrings["Storage"] = "Storage"; 957 960 localizedStrings["Style Attribute"] = "Style Attribute"; 961 localizedStrings["Style Resolution"] = "Style Resolution"; 958 962 localizedStrings["Style rule"] = "Style rule"; 959 963 localizedStrings["Styles"] = "Styles"; … … 1001 1005 localizedStrings["This object is referenced by internal objects"] = "This object is referenced by internal objects"; 1002 1006 localizedStrings["This text resource could benefit from compression"] = "This text resource could benefit from compression"; 1007 localizedStrings["Threads"] = "Threads"; 1003 1008 localizedStrings["Time"] = "Time"; 1009 localizedStrings["Time spent on the main thread"] = "Time spent on the main thread"; 1004 1010 localizedStrings["Time to First Byte"] = "Time to First Byte"; 1005 1011 localizedStrings["Timeline"] = "Timeline"; … … 1017 1023 localizedStrings["Toggle Visibility"] = "Toggle Visibility"; 1018 1024 localizedStrings["Top Functions"] = "Top Functions"; 1025 localizedStrings["Total"] = "Total"; 1019 1026 localizedStrings["Total Time"] = "Total Time"; 1020 1027 localizedStrings["Total memory size at the end of the selected time range"] = "Total memory size at the end of the selected time range"; … … 1075 1082 localizedStrings["Waterfall"] = "Waterfall"; 1076 1083 localizedStrings["Web Inspector"] = "Web Inspector"; 1084 localizedStrings["WebKit Threads"] = "WebKit Threads"; 1077 1085 localizedStrings["WebRTC"] = "WebRTC"; 1078 1086 localizedStrings["WebRTC Logging:"] = "WebRTC Logging:"; … … 1082 1090 localizedStrings["With Object Properties"] = "With Object Properties"; 1083 1091 localizedStrings["Worker"] = "Worker"; 1092 localizedStrings["Worker Thread"] = "Worker Thread"; 1084 1093 localizedStrings["Worker \u2014 %s"] = "Worker \u2014 %s"; 1085 1094 localizedStrings["Working Copy"] = "Working Copy"; -
trunk/Source/WebInspectorUI/UserInterface/Main.html
r242049 r242073 46 46 <link rel="stylesheet" href="Views/CPUTimelineOverviewGraph.css"> 47 47 <link rel="stylesheet" href="Views/CPUTimelineView.css"> 48 <link rel="stylesheet" href="Views/CPUUsageStackedView.css"> 48 49 <link rel="stylesheet" href="Views/CPUUsageView.css"> 49 50 <link rel="stylesheet" href="Views/CallFrameIcons.css"> … … 593 594 <script src="Views/CPUTimelineOverviewGraph.js"></script> 594 595 <script src="Views/CPUTimelineView.js"></script> 596 <script src="Views/CPUUsageStackedView.js"></script> 595 597 <script src="Views/CPUUsageView.js"></script> 596 598 <script src="Views/CSSStyleSheetTreeElement.js"></script> … … 807 809 <script src="Views/SVGImageResourceClusterContentView.js"></script> 808 810 <script src="Views/StackTraceView.js"></script> 811 <script src="Views/StackedColumnChart.js"></script> 809 812 <script src="Views/StackedLineChart.js"></script> 810 813 <script src="Views/StorageSidebarPanel.js"></script> -
trunk/Source/WebInspectorUI/UserInterface/Models/CPUTimelineRecord.js
r241739 r242073 44 44 this._workerThreadUsage = 0; 45 45 this._unknownThreadUsage = 0; 46 this._workersData = null; 46 47 47 48 for (let thread of threads) { … … 53 54 54 55 if (thread.type === InspectorBackend.domains.CPUProfiler.ThreadInfoType.WebKit) { 55 if (thread.targetId) 56 if (thread.targetId) { 57 if (!this._workersData) 58 this._workersData = []; 59 this._workersData.push(thread); 56 60 this._workerThreadUsage += thread.usage; 57 else 58 this._webkitThreadUsage += thread.usage; 61 continue; 62 } 63 64 this._webkitThreadUsage += thread.usage; 59 65 continue; 60 66 } … … 73 79 get workerThreadUsage() { return this._workerThreadUsage; } 74 80 get unknownThreadUsage() { return this._unknownThreadUsage; } 81 get workersData() { return this._workersData; } 75 82 }; -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.css
r242072 r242073 58 58 } 59 59 60 body[dir=rtl] .timeline-overview-graph.cpu > .stacked-column-chart { 61 transform: scaleX(-1); 62 } 63 64 .timeline-overview-graph.cpu > .stacked-column-chart > svg > rect { 65 stroke: var(--cpu-stroke-color); 66 fill: var(--cpu-fill-color); 67 } 68 69 .timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.main-thread-usage { 70 fill: var(--cpu-main-thread-fill-color); 71 } 72 73 .timeline-overview-graph.cpu > .stacked-column-chart > svg > rect.worker-thread-usage { 74 fill: var(--cpu-worker-thread-fill-color); 75 } 76 77 /* LegacyCPUTimeline */ 78 .timeline-overview-graph.cpu > .column-chart > svg > rect { 79 stroke: var(--cpu-stroke-color); 80 fill: var(--cpu-main-thread-fill-color); 81 } 82 60 83 body[dir=rtl] .timeline-overview-graph.cpu > .column-chart { 61 84 transform: scaleX(-1); 62 85 } 63 64 .timeline-overview-graph.cpu > .column-chart > svg > rect {65 stroke: var(--cpu-stroke-color);66 fill: var(--cpu-fill-color);67 } -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineOverviewGraph.js
r240872 r242073 39 39 40 40 let size = new WI.Size(0, this.height); 41 this._chart = new WI.ColumnChart(size); 41 if (WI.settings.experimentalEnableCPUUsageEnhancements.value) { 42 this._chart = new WI.StackedColumnChart(size); 43 this._chart.initializeSections(["main-thread-usage", "worker-thread-usage", "total-usage"]); 44 } else 45 this._chart = new WI.ColumnChart(size); 42 46 this.addSubview(this._chart); 43 47 this.element.appendChild(this._chart.element); … … 115 119 for (let record of visibleRecords) { 116 120 let w = intervalWidth; 117 let h = Math.max(minimumDisplayHeight, yScale(record.usage));121 let h3 = Math.max(minimumDisplayHeight, yScale(record.usage)); 118 122 let x = xScale(record.startTime - (samplingRatePerSecond / 2)); 119 let y = height - h; 120 this._chart.addColumn(x, y, w, h); 123 if (WI.settings.experimentalEnableCPUUsageEnhancements.value) { 124 let h1 = Math.max(minimumDisplayHeight, yScale(record.mainThreadUsage)); 125 let h2 = Math.max(minimumDisplayHeight, yScale(record.mainThreadUsage + record.workerThreadUsage)); 126 this._chart.addColumnSet(x, height, w, [h1, h2, h3]); 127 } else 128 this._chart.addColumn(x, height - h3, w, h3); 121 129 } 122 123 this._chart.updateLayout();124 130 } 125 131 -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.css
r240763 r242073 28 28 } 29 29 30 .timeline-view.cpu > .content { 31 margin-top: 10px; 30 .timeline-view.cpu > .content > .overview { 31 display: flex; 32 justify-content: center; 33 margin-bottom: 10px; 34 padding: 10px; 35 border-bottom: 1px solid var(--border-color); 32 36 } 33 37 … … 64 68 } 65 69 66 .cpu-usage-view .line-chart > svg > path { 70 .timeline-view.cpu > .content > .details > .subtitle.threads { 71 position: relative; 72 z-index: calc(var(--timeline-marker-z-index) + 1); 73 padding-top: 10px; 74 background-color: var(--background-color-content); 75 } 76 77 .timeline-view.cpu > .content > .overview > .chart { 78 width: 420px; 79 text-align: center; 80 } 81 82 .timeline-view.cpu > .content > .overview > .chart > .subtitle { 83 margin-bottom: 1em; 84 } 85 86 .timeline-view.cpu > .content > .overview > .chart > .container { 87 display: flex; 88 justify-content: center; 89 } 90 91 .timeline-view.cpu > .content > .overview .samples, 92 .timeline-view.cpu > .content > .overview .legend .size { 93 margin: auto; 94 color: var(--text-color-secondary); 95 } 96 97 .timeline-view.cpu .legend { 98 -webkit-padding-start: 20px; 99 text-align: start; 100 } 101 102 .timeline-view.cpu .legend .row { 103 display: flex; 104 } 105 106 .timeline-view.cpu .legend .row + .row { 107 margin-top: 4px; 108 } 109 110 .timeline-view.cpu .legend .swatch { 111 width: 1em; 112 height: 1em; 113 margin-top: 1px; 114 -webkit-margin-end: 8px; 115 } 116 117 .timeline-view.cpu .legend > .row > .swatch.sample-type-idle { 118 border: 1px solid var(--cpu-idle-stroke-color); 119 background-color: var(--cpu-idle-fill-color); 120 } 121 122 .timeline-view.cpu .legend > .row > .swatch.sample-type-script { 123 border: 1px solid var(--cpu-script-stroke-color); 124 background-color: var(--cpu-script-fill-color); 125 } 126 127 .timeline-view.cpu .legend > .row > .swatch.sample-type-style { 128 border: 1px solid var(--cpu-style-stroke-color); 129 background-color: var(--cpu-style-fill-color); 130 } 131 132 .timeline-view.cpu .legend > .row > .swatch.sample-type-layout { 133 border: 1px solid var(--cpu-layout-stroke-color); 134 background-color: var(--cpu-layout-fill-color); 135 } 136 137 .timeline-view.cpu .legend > .row > .swatch.sample-type-paint { 138 border: 1px solid var(--cpu-paint-stroke-color); 139 background-color: var(--cpu-paint-fill-color); 140 } 141 142 .timeline-view.cpu .circle-chart > svg > path.segment.sample-type-idle { 143 stroke: var(--cpu-idle-stroke-color); 144 fill: var(--cpu-idle-fill-color); 145 } 146 147 .timeline-view.cpu .circle-chart > svg > path.segment.sample-type-script { 148 stroke: var(--cpu-script-stroke-color); 149 fill: var(--cpu-script-fill-color); 150 } 151 152 .timeline-view.cpu .circle-chart > svg > path.segment.sample-type-style { 153 stroke: var(--cpu-style-stroke-color); 154 fill: var(--cpu-style-fill-color); 155 } 156 157 .timeline-view.cpu .circle-chart > svg > path.segment.sample-type-layout { 158 stroke: var(--cpu-layout-stroke-color); 159 fill: var(--cpu-layout-fill-color); 160 } 161 162 .timeline-view.cpu .circle-chart > svg > path.segment.sample-type-paint { 163 stroke: var(--cpu-paint-stroke-color); 164 fill: var(--cpu-paint-fill-color); 165 } 166 167 .timeline-view.cpu svg > path { 67 168 stroke: var(--cpu-stroke-color); 68 169 fill: var(--cpu-fill-color); 69 170 } 70 171 71 .timeline-view.cpu .legend > .row > .swatch.current { 72 border: 1px solid var(--cpu-max-comparison-stroke-color); 73 background: var(--cpu-max-comparison-fill-color); 74 } 172 .timeline-view.cpu .main-thread svg > path, 173 .timeline-view.cpu svg > path.main-thread-usage { 174 fill: var(--cpu-main-thread-fill-color); 175 } 176 177 .timeline-view.cpu .worker-thread svg > path, 178 .timeline-view.cpu svg > path.worker-thread-usage { 179 fill: var(--cpu-worker-thread-fill-color); 180 } 181 182 .timeline-view.cpu .cpu-usage-view.empty { 183 display: none; 184 } 185 186 .timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers { 187 position: absolute; 188 top: 0; 189 left: 0; 190 right: 0; 191 bottom: 0; 192 pointer-events: none; 193 } 194 195 .timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div { 196 position: absolute; 197 z-index: 10; 198 width: 100%; 199 height: 1px; 200 background-color: hsla(0, 0%, var(--foreground-lightness), 0.07); 201 } 202 203 body[dir=ltr] .timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div { 204 text-align: end; 205 } 206 207 body[dir=rtl] .timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div { 208 transform: scaleX(-1); 209 } 210 211 .timeline-view.cpu :matches(.line-chart, .stacked-line-chart) .markers > div > .label { 212 padding: 2px; 213 font-size: 8px; 214 color: var(--text-color-secondary); 215 background-color: var(--background-color-content); 216 } -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUTimelineView.js
r241301 r242073 36 36 this.element.classList.add("cpu"); 37 37 38 timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._cpuTimelineRecordAdded, this); 39 } 40 41 // Static 42 43 static displayNameForSampleType(type) 44 { 45 switch (type) { 46 case WI.CPUTimelineView.SampleType.Script: 47 return WI.UIString("Script"); 48 case WI.CPUTimelineView.SampleType.Layout: 49 return WI.UIString("Layout"); 50 case WI.CPUTimelineView.SampleType.Paint: 51 return WI.UIString("Paint"); 52 case WI.CPUTimelineView.SampleType.Style: 53 return WI.UIString("Style Resolution"); 54 } 55 console.error("Unknown sample type", type); 56 } 57 58 static get cpuUsageViewHeight() { return 150; } 59 static get threadCPUUsageViewHeight() { return 65; } 60 61 // Public 62 63 shown() 64 { 65 super.shown(); 66 67 if (this._timelineRuler) 68 this._timelineRuler.updateLayout(WI.View.LayoutReason.Resize); 69 } 70 71 closed() 72 { 73 console.assert(this.representedObject instanceof WI.Timeline); 74 this.representedObject.removeEventListener(null, null, this); 75 } 76 77 reset() 78 { 79 super.reset(); 80 81 this.clear(); 82 } 83 84 clear() 85 { 86 if (!this.didInitialLayout) 87 return; 88 89 this._breakdownChart.clear(); 90 this._breakdownChart.needsLayout(); 91 this._clearBreakdownLegend(); 92 93 function clearUsageView(view) { 94 view.clear(); 95 96 let markersElement = view.chart.element.querySelector(".markers"); 97 if (markersElement) 98 markersElement.remove(); 99 } 100 101 clearUsageView(this._cpuUsageView); 102 clearUsageView(this._mainThreadUsageView); 103 clearUsageView(this._webkitThreadUsageView); 104 clearUsageView(this._unknownThreadUsageView); 105 106 this._removeWorkerThreadViews(); 107 } 108 109 get scrollableElements() 110 { 111 return [this.element]; 112 } 113 114 // Protected 115 116 get showsFilterBar() { return false; } 117 118 initialLayout() 119 { 120 this.element.style.setProperty("--cpu-usage-stacked-view-height", CPUTimelineView.cpuUsageViewHeight + "px"); 121 this.element.style.setProperty("--cpu-usage-view-height", CPUTimelineView.threadCPUUsageViewHeight + "px"); 122 38 123 let contentElement = this.element.appendChild(document.createElement("div")); 39 124 contentElement.classList.add("content"); 40 125 41 // FIXME: Overview with charts. 126 let overviewElement = contentElement.appendChild(document.createElement("div")); 127 overviewElement.classList.add("overview"); 128 129 function createChartContainer(parentElement, subtitle, tooltip) { 130 let chartElement = parentElement.appendChild(document.createElement("div")); 131 chartElement.classList.add("chart"); 132 133 let chartSubtitleElement = chartElement.appendChild(document.createElement("div")); 134 chartSubtitleElement.classList.add("subtitle"); 135 chartSubtitleElement.textContent = subtitle; 136 chartSubtitleElement.title = tooltip; 137 138 let chartFlexContainerElement = chartElement.appendChild(document.createElement("div")); 139 chartFlexContainerElement.classList.add("container"); 140 return chartFlexContainerElement; 141 } 142 143 function appendLegendRow(legendElement, sampleType) { 144 let rowElement = legendElement.appendChild(document.createElement("div")); 145 rowElement.classList.add("row"); 146 147 let swatchElement = rowElement.appendChild(document.createElement("div")); 148 swatchElement.classList.add("swatch", sampleType); 149 150 let valueContainer = rowElement.appendChild(document.createElement("div")); 151 valueContainer.classList.add("value"); 152 153 let labelElement = valueContainer.appendChild(document.createElement("div")); 154 labelElement.classList.add("label"); 155 labelElement.textContent = WI.CPUTimelineView.displayNameForSampleType(sampleType); 156 157 let sizeElement = valueContainer.appendChild(document.createElement("div")); 158 sizeElement.classList.add("size"); 159 160 return sizeElement; 161 } 162 163 let breakdownChartContainerElement = createChartContainer(overviewElement, WI.UIString("Main Thread"), WI.UIString("Breakdown of time spent on the main thread")); 164 this._breakdownChart = new WI.CircleChart({size: 120, innerRadiusRatio: 0.5}); 165 this._breakdownChart.segments = Object.values(WI.CPUTimelineView.SampleType); 166 this.addSubview(this._breakdownChart); 167 breakdownChartContainerElement.appendChild(this._breakdownChart.element); 168 169 this._breakdownLegendElement = breakdownChartContainerElement.appendChild(document.createElement("div")); 170 this._breakdownLegendElement.classList.add("legend"); 171 172 this._breakdownLegendScriptElement = appendLegendRow(this._breakdownLegendElement, WI.CPUTimelineView.SampleType.Script); 173 this._breakdownLegendLayoutElement = appendLegendRow(this._breakdownLegendElement, WI.CPUTimelineView.SampleType.Layout); 174 this._breakdownLegendPaintElement = appendLegendRow(this._breakdownLegendElement, WI.CPUTimelineView.SampleType.Paint); 175 this._breakdownLegendStyleElement = appendLegendRow(this._breakdownLegendElement, WI.CPUTimelineView.SampleType.Style); 42 176 43 177 let detailsContainerElement = this._detailsContainerElement = contentElement.appendChild(document.createElement("div")); … … 45 179 46 180 this._timelineRuler = new WI.TimelineRuler; 181 this._timelineRuler.zeroTime = this.zeroTime; 182 this._timelineRuler.startTime = this.startTime; 183 this._timelineRuler.endTime = this.endTime; 184 47 185 this.addSubview(this._timelineRuler); 48 186 detailsContainerElement.appendChild(this._timelineRuler.element); 187 188 // Cause the TimelineRuler to layout now so we will have some of its 189 // important properties initialized for our layout. 190 this._timelineRuler.updateLayout(WI.View.LayoutReason.Resize); 49 191 50 192 let detailsSubtitleElement = detailsContainerElement.appendChild(document.createElement("div")); … … 52 194 detailsSubtitleElement.textContent = WI.UIString("CPU Usage"); 53 195 54 this._cpuUsageView = new WI.CPUUsage View;196 this._cpuUsageView = new WI.CPUUsageStackedView(WI.UIString("Total")); 55 197 this.addSubview(this._cpuUsageView); 56 198 this._detailsContainerElement.appendChild(this._cpuUsageView.element); 57 199 58 timeline.addEventListener(WI.Timeline.Event.RecordAdded, this._cpuTimelineRecordAdded, this); 59 } 60 61 // Public 62 63 shown() 64 { 65 super.shown(); 66 67 this._timelineRuler.updateLayout(WI.View.LayoutReason.Resize); 68 } 69 70 closed() 71 { 72 console.assert(this.representedObject instanceof WI.Timeline); 73 this.representedObject.removeEventListener(null, null, this); 74 } 75 76 reset() 77 { 78 super.reset(); 79 80 this.clear(); 81 } 82 83 clear() 84 { 85 this._cpuUsageView.clear(); 86 } 87 88 get scrollableElements() 89 { 90 return [this.element]; 91 } 92 93 // Protected 94 95 get showsFilterBar() { return false; } 200 let threadsSubtitleElement = detailsContainerElement.appendChild(document.createElement("div")); 201 threadsSubtitleElement.classList.add("subtitle", "threads"); 202 threadsSubtitleElement.textContent = WI.UIString("Threads"); 203 204 this._mainThreadUsageView = new WI.CPUUsageView(WI.UIString("Main Thread")); 205 this._mainThreadUsageView.element.classList.add("main-thread"); 206 this.addSubview(this._mainThreadUsageView); 207 this._detailsContainerElement.appendChild(this._mainThreadUsageView.element); 208 209 this._webkitThreadUsageView = new WI.CPUUsageView(WI.UIString("WebKit Threads")); 210 this._webkitThreadUsageView.element.classList.add("non-main-thread"); 211 this.addSubview(this._webkitThreadUsageView); 212 this._detailsContainerElement.appendChild(this._webkitThreadUsageView.element); 213 214 this._unknownThreadUsageView = new WI.CPUUsageView(WI.UIString("Other Threads")); 215 this._unknownThreadUsageView.element.classList.add("non-main-thread"); 216 this.addSubview(this._unknownThreadUsageView); 217 this._detailsContainerElement.appendChild(this._unknownThreadUsageView.element); 218 219 this._workerViews = []; 220 } 96 221 97 222 layout() … … 105 230 this._timelineRuler.endTime = this.endTime; 106 231 107 const cpuUsageViewHeight = 75; // Keep this in sync with .cpu-usage-view 232 let secondsPerPixel = this._timelineRuler.secondsPerPixel; 233 if (!secondsPerPixel) 234 return; 108 235 109 236 let graphStartTime = this.startTime; 110 237 let graphEndTime = this.endTime; 111 let secondsPerPixel = this._timelineRuler.secondsPerPixel;112 238 let visibleEndTime = Math.min(this.endTime, this.currentTime); 113 239 114 240 let discontinuities = this._recording.discontinuitiesInTimeRange(graphStartTime, visibleEndTime); 241 let originalDiscontinuities = discontinuities.slice(); 115 242 116 243 // Don't include the record before the graph start if the graph start is within a gap. … … 122 249 } 123 250 124 // Update total usage chart with the last record's data. 125 let lastRecord = visibleRecords.lastValue; 126 127 // FIXME: Left chart. 128 // FIXME: Right chart. 251 let samplingData = this._computeSamplingData(graphStartTime, visibleEndTime); 252 let nonIdleSamplesCount = samplingData.samples.length - samplingData.samplesIdle; 253 if (!nonIdleSamplesCount) { 254 this._breakdownChart.clear(); 255 this._breakdownChart.needsLayout(); 256 this._clearBreakdownLegend(); 257 } else { 258 let percentScript = samplingData.samplesScript / nonIdleSamplesCount; 259 let percentLayout = samplingData.samplesLayout / nonIdleSamplesCount; 260 let percentPaint = samplingData.samplesPaint / nonIdleSamplesCount; 261 let percentStyle = samplingData.samplesStyle / nonIdleSamplesCount; 262 263 this._breakdownLegendScriptElement.textContent = `${Number.percentageString(percentScript)} (${samplingData.samplesScript})`; 264 this._breakdownLegendLayoutElement.textContent = `${Number.percentageString(percentLayout)} (${samplingData.samplesLayout})`; 265 this._breakdownLegendPaintElement.textContent = `${Number.percentageString(percentPaint)} (${samplingData.samplesPaint})`; 266 this._breakdownLegendStyleElement.textContent = `${Number.percentageString(percentStyle)} (${samplingData.samplesStyle})`; 267 268 this._breakdownChart.values = [percentScript * 100, percentLayout * 100, percentPaint * 100, percentStyle * 100]; 269 this._breakdownChart.needsLayout(); 270 271 let centerElement = this._breakdownChart.centerElement; 272 let samplesElement = centerElement.firstChild; 273 if (!samplesElement) { 274 samplesElement = centerElement.appendChild(document.createElement("div")); 275 samplesElement.classList.add("samples"); 276 samplesElement.title = WI.UIString("Time spent on the main thread"); 277 } 278 279 let millisecondsStringNoDecimal = WI.UIString("%.0fms").format(nonIdleSamplesCount); 280 samplesElement.textContent = millisecondsStringNoDecimal; 281 } 129 282 130 283 let dataPoints = []; 284 let workersDataMap = new Map; 285 131 286 let max = -Infinity; 287 let mainThreadMax = -Infinity; 288 let webkitThreadMax = -Infinity; 289 let unknownThreadMax = -Infinity; 290 132 291 let min = Infinity; 292 let mainThreadMin = Infinity; 293 let webkitThreadMin = Infinity; 294 let unknownThreadMin = Infinity; 295 133 296 let average = 0; 297 let mainThreadAverage = 0; 298 let webkitThreadAverage = 0; 299 let unknownThreadAverage = 0; 134 300 135 301 for (let record of visibleRecords) { 136 302 let time = record.startTime; 137 let usage = record.usage;303 let {usage, mainThreadUsage, workerThreadUsage, webkitThreadUsage, unknownThreadUsage} = record; 138 304 139 305 if (discontinuities.length && discontinuities[0].endTime < time) { … … 142 308 while (discontinuities.length && discontinuities[0].endTime < time) 143 309 endDiscontinuity = discontinuities.shift(); 144 dataPoints.push({time: startDiscontinuity.startTime, size: 0}); 145 dataPoints.push({time: endDiscontinuity.endTime, size: 0}); 146 dataPoints.push({time: endDiscontinuity.endTime, size: usage}); 147 } 148 149 dataPoints.push({time, size: usage}); 310 311 if (dataPoints.length) { 312 let previousDataPoint = dataPoints.lastValue; 313 dataPoints.push({ 314 time: startDiscontinuity.startTime, 315 mainThreadUsage: previousDataPoint.mainThreadUsage, 316 workerThreadUsage: previousDataPoint.workerThreadUsage, 317 webkitThreadUsage: previousDataPoint.webkitThreadUsage, 318 unknownThreadUsage: previousDataPoint.unknownThreadUsage, 319 usage: previousDataPoint.usage, 320 }); 321 } 322 323 dataPoints.push({time: startDiscontinuity.startTime, mainThreadUsage: 0, workerThreadUsage: 0, webkitThreadUsage: 0, unknownThreadUsage: 0, usage: 0}); 324 dataPoints.push({time: endDiscontinuity.endTime, mainThreadUsage: 0, workerThreadUsage: 0, webkitThreadUsage: 0, unknownThreadUsage: 0, usage: 0}); 325 dataPoints.push({time: endDiscontinuity.endTime, mainThreadUsage, workerThreadUsage, webkitThreadUsage, unknownThreadUsage, usage}); 326 } 327 328 dataPoints.push({time, mainThreadUsage, workerThreadUsage, webkitThreadUsage, unknownThreadUsage, usage}); 329 150 330 max = Math.max(max, usage); 331 mainThreadMax = Math.max(mainThreadMax, mainThreadUsage); 332 webkitThreadMax = Math.max(webkitThreadMax, webkitThreadUsage); 333 unknownThreadMax = Math.max(unknownThreadMax, unknownThreadUsage); 334 151 335 min = Math.min(min, usage); 336 mainThreadMin = Math.min(mainThreadMin, mainThreadUsage); 337 webkitThreadMin = Math.min(webkitThreadMin, webkitThreadUsage); 338 unknownThreadMin = Math.min(unknownThreadMin, unknownThreadUsage); 339 152 340 average += usage; 341 mainThreadAverage += mainThreadUsage; 342 webkitThreadAverage += webkitThreadUsage; 343 unknownThreadAverage += unknownThreadUsage; 344 345 if (record.workersData && record.workersData.length) { 346 for (let {targetId, usage} of record.workersData) { 347 let workerData = workersDataMap.get(targetId); 348 if (!workerData) { 349 workerData = { 350 discontinuities: originalDiscontinuities.slice(), 351 recordsCount: 0, 352 dataPoints: [], 353 min: Infinity, 354 max: -Infinity, 355 average: 0 356 }; 357 358 while (workerData.discontinuities.length && workerData.discontinuities[0].endTime <= graphStartTime) 359 workerData.discontinuities.shift(); 360 workerData.dataPoints.push({time: graphStartTime, usage: 0}); 361 workerData.dataPoints.push({time, usage: 0}); 362 workersDataMap.set(targetId, workerData); 363 } 364 365 if (workerData.discontinuities.length && workerData.discontinuities[0].endTime < time) { 366 let startDiscontinuity = workerData.discontinuities.shift(); 367 let endDiscontinuity = startDiscontinuity; 368 while (workerData.discontinuities.length && workerData.discontinuities[0].endTime < time) 369 endDiscontinuity = workerData.discontinuities.shift(); 370 if (workerData.dataPoints.length) { 371 let previousDataPoint = workerData.dataPoints.lastValue; 372 workerData.dataPoints.push({time: startDiscontinuity.startTime, usage: previousDataPoint.usage}); 373 } 374 workerData.dataPoints.push({time: startDiscontinuity.startTime, usage: 0}); 375 workerData.dataPoints.push({time: endDiscontinuity.endTime, usage: 0}); 376 workerData.dataPoints.push({time: endDiscontinuity.endTime, usage}); 377 } 378 379 workerData.dataPoints.push({time, usage}); 380 workerData.recordsCount += 1; 381 workerData.max = Math.max(workerData.max, usage); 382 workerData.min = Math.min(workerData.min, usage); 383 workerData.average += usage; 384 } 385 } 153 386 } 154 387 155 388 average /= visibleRecords.length; 389 mainThreadAverage /= visibleRecords.length; 390 webkitThreadAverage /= visibleRecords.length; 391 unknownThreadAverage /= visibleRecords.length; 392 393 for (let [workerId, workerData] of workersDataMap) 394 workerData.average = workerData.average / workerData.recordsCount; 156 395 157 396 // If the graph end time is inside a gap, the last data point should … … 160 399 visibleEndTime = discontinuities[0].startTime; 161 400 162 function layoutView(view, {dataPoints, min, max, average}) { 401 function removeGreaterThan(arr, max) { 402 return arr.filter((x) => x <= max); 403 } 404 405 function markerValuesForMaxValue(max) { 406 if (max < 1) 407 return [0.5]; 408 if (max < 7) 409 return removeGreaterThan([1, 3, 5], max); 410 if (max < 12.5) 411 return removeGreaterThan([5, 10], max); 412 if (max < 20) 413 return removeGreaterThan([5, 10, 15], max); 414 if (max < 30) 415 return removeGreaterThan([10, 20, 30], max); 416 if (max < 50) 417 return removeGreaterThan([15, 30, 45], max); 418 if (max < 100) 419 return removeGreaterThan([25, 50, 75], max); 420 if (max < 200) 421 return removeGreaterThan([50, 100, 150], max); 422 if (max >= 200) { 423 let hundreds = Math.floor(max / 100); 424 let even = (hundreds % 2) === 0; 425 if (even) { 426 let top = hundreds * 100; 427 let bottom = top / 2; 428 return [bottom, top]; 429 } 430 let top = hundreds * 100; 431 let bottom = 100; 432 let mid = (top + bottom) / 2; 433 return [bottom, mid, top]; 434 } 435 } 436 437 // Layout all graphs to the same time scale. The maximum value is 438 // the maximum total CPU usage across all threads. 439 let layoutMax = max; 440 441 function layoutView(view, property, graphHeight, {dataPoints, min, max, average}) { 163 442 if (min === Infinity) 164 443 min = 0; 165 444 if (max === -Infinity) 166 445 max = 0; 167 168 // Zoom in to the top of each graph to accentuate small changes. 169 let graphMin = min * 0.95; 170 let graphMax = (max * 1.05) - graphMin; 446 if (layoutMax === -Infinity) 447 layoutMax = 0; 448 449 let isAllThreadsGraph = property === null; 450 451 let graphMax = layoutMax * 1.05; 171 452 172 453 function xScale(time) { … … 174 455 } 175 456 176 let size = new WI.Size(xScale(graphEndTime), cpuUsageViewHeight);457 let size = new WI.Size(xScale(graphEndTime), graphHeight); 177 458 178 459 function yScale(value) { 179 return size.height - (((value - graphMin) / graphMax) * size.height); 180 } 181 182 view.updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale); 183 } 184 185 layoutView(this._cpuUsageView, {dataPoints, min, max, average}); 460 return size.height - ((value / graphMax) * size.height); 461 } 462 463 view.updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale, property); 464 465 let markersElement = view.chart.element.querySelector(".markers"); 466 if (!markersElement) { 467 markersElement = view.chart.element.appendChild(document.createElement("div")); 468 markersElement.className = "markers"; 469 } 470 markersElement.removeChildren(); 471 472 let markerValues; 473 if (isAllThreadsGraph) 474 markerValues = markerValuesForMaxValue(max); 475 else { 476 const minimumMarkerTextHeight = 17; 477 let percentPerPixel = 1 / (graphHeight / layoutMax); 478 if (layoutMax < 5) { 479 let minimumDisplayablePercentByTwo = Math.ceil((minimumMarkerTextHeight * percentPerPixel) / 2) * 2; 480 markerValues = [Math.max(minimumDisplayablePercentByTwo, Math.floor(max))]; 481 } else { 482 let minimumDisplayablePercentByFive = Math.ceil((minimumMarkerTextHeight * percentPerPixel) / 5) * 5; 483 markerValues = [Math.max(minimumDisplayablePercentByFive, Math.floor(max))]; 484 } 485 } 486 487 for (let value of markerValues) { 488 let marginTop = yScale(value); 489 490 let markerElement = markersElement.appendChild(document.createElement("div")); 491 markerElement.style.marginTop = marginTop.toFixed(2) + "px"; 492 493 let labelElement = markerElement.appendChild(document.createElement("span")); 494 labelElement.classList.add("label"); 495 const precision = 0; 496 labelElement.innerText = Number.percentageString(value / 100, precision); 497 } 498 } 499 500 layoutView(this._cpuUsageView, null, CPUTimelineView.cpuUsageViewHeight, {dataPoints, min, max, average}); 501 layoutView(this._mainThreadUsageView, "mainThreadUsage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints, min: mainThreadMin, max: mainThreadMax, average: mainThreadAverage}); 502 layoutView(this._webkitThreadUsageView, "webkitThreadUsage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints, min: webkitThreadMin, max: webkitThreadMax, average: webkitThreadAverage}); 503 layoutView(this._unknownThreadUsageView, "unknownThreadUsage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints, min: unknownThreadMin, max: unknownThreadMax, average: unknownThreadAverage}); 504 505 this._removeWorkerThreadViews(); 506 507 for (let [workerId, workerData] of workersDataMap) { 508 let worker = WI.targetManager.targetForIdentifier(workerId); 509 let displayName = worker ? worker.displayName : WI.UIString("Worker Thread"); 510 let workerView = new WI.CPUUsageView(displayName); 511 workerView.element.classList.add("worker-thread"); 512 this.addSubview(workerView); 513 this._detailsContainerElement.insertBefore(workerView.element, this._webkitThreadUsageView.element); 514 this._workerViews.push(workerView); 515 516 layoutView(workerView, "usage", CPUTimelineView.threadCPUUsageViewHeight, {dataPoints: workerData.dataPoints, min: workerData.min, max: workerData.max, average: workerData.average}); 517 } 186 518 } 187 519 188 520 // Private 521 522 _computeSamplingData(startTime, endTime) 523 { 524 // Compute per-millisecond samples of what the main thread was doing. 525 // We construct an array for every millisecond between the start and end time 526 // and mark each millisecond with the best representation of the work that 527 // was being done at that time. We start by populating the samples with 528 // all of the script periods and then override with layout and rendering 529 // samples. This means a forced layout would be counted as a layout: 530 // 531 // Initial: [ ------, ------, ------, ------, ------ ] 532 // Script Samples: [ ------, Script, Script, Script, ------ ] 533 // Layout Samples: [ ------, Script, Layout, Script, ------ ] 534 // 535 // The undefined samples are considered Idle, but in actuality WebKit 536 // may have been doing some work (such as hit testing / inspector protocol) 537 // that is not included it in generic Timeline data. This just works with 538 // with the data available to the frontend and is quite accurate for most 539 // Main Thread activity. 540 541 const includeRecordBeforeStart = true; 542 543 let scriptTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Script); 544 let scriptRecords = scriptTimeline ? scriptTimeline.recordsInTimeRange(startTime, endTime, includeRecordBeforeStart) : []; 545 scriptRecords = scriptRecords.filter((record) => { 546 switch (record.eventType) { 547 case WI.ScriptTimelineRecord.EventType.ScriptEvaluated: 548 case WI.ScriptTimelineRecord.EventType.APIScriptEvaluated: 549 case WI.ScriptTimelineRecord.EventType.ObserverCallback: 550 case WI.ScriptTimelineRecord.EventType.EventDispatched: 551 case WI.ScriptTimelineRecord.EventType.MicrotaskDispatched: 552 case WI.ScriptTimelineRecord.EventType.TimerFired: 553 case WI.ScriptTimelineRecord.EventType.AnimationFrameFired: 554 // These event types define script entry/exits. 555 return true; 556 557 case WI.ScriptTimelineRecord.EventType.AnimationFrameRequested: 558 case WI.ScriptTimelineRecord.EventType.AnimationFrameCanceled: 559 case WI.ScriptTimelineRecord.EventType.TimerInstalled: 560 case WI.ScriptTimelineRecord.EventType.TimerRemoved: 561 case WI.ScriptTimelineRecord.EventType.ProbeSampleRecorded: 562 case WI.ScriptTimelineRecord.EventType.ConsoleProfileRecorded: 563 case WI.ScriptTimelineRecord.EventType.GarbageCollected: 564 // These event types have no time range, or are contained by the others. 565 return false; 566 567 default: 568 console.error("Unhandled ScriptTimelineRecord.EventType", record.eventType); 569 return false; 570 } 571 }); 572 573 let layoutTimeline = this._recording.timelineForRecordType(WI.TimelineRecord.Type.Layout); 574 let layoutRecords = layoutTimeline ? layoutTimeline.recordsInTimeRange(startTime, endTime, includeRecordBeforeStart) : []; 575 layoutRecords = layoutRecords.filter((record) => { 576 switch (record.eventType) { 577 case WI.LayoutTimelineRecord.EventType.RecalculateStyles: 578 case WI.LayoutTimelineRecord.EventType.ForcedLayout: 579 case WI.LayoutTimelineRecord.EventType.Layout: 580 case WI.LayoutTimelineRecord.EventType.Paint: 581 case WI.LayoutTimelineRecord.EventType.Composite: 582 // These event types define layout and rendering entry/exits. 583 return true; 584 585 case WI.LayoutTimelineRecord.EventType.InvalidateStyles: 586 case WI.LayoutTimelineRecord.EventType.InvalidateLayout: 587 // These event types have no time range. 588 return false; 589 590 default: 591 console.error("Unhandled LayoutTimelineRecord.EventType", record.eventType); 592 return false; 593 } 594 }); 595 596 let millisecondStartTime = Math.round(startTime * 1000); 597 let millisecondEndTime = Math.round(endTime * 1000); 598 let millisecondDuration = millisecondEndTime - millisecondStartTime; 599 600 let samples = new Array(millisecondDuration); 601 602 function markRecordEntries(records, callback) { 603 for (let record of records) { 604 let recordStart = Math.round(record.startTime * 1000); 605 let recordEnd = Math.round(record.endTime * 1000); 606 if (recordStart > millisecondEndTime) 607 continue; 608 if (recordEnd < millisecondStartTime) 609 continue; 610 611 let offset = recordStart - millisecondStartTime; 612 recordStart = Math.max(recordStart, millisecondStartTime); 613 recordEnd = Math.min(recordEnd, millisecondEndTime); 614 615 let value = callback(record); 616 for (let t = recordStart; t <= recordEnd; ++t) 617 samples[t - millisecondStartTime] = value; 618 } 619 } 620 621 markRecordEntries(scriptRecords, (record) => { 622 return WI.CPUTimelineView.SampleType.Script; 623 }); 624 625 markRecordEntries(layoutRecords, (record) => { 626 switch (record.eventType) { 627 case WI.LayoutTimelineRecord.EventType.RecalculateStyles: 628 return WI.CPUTimelineView.SampleType.Style; 629 case WI.LayoutTimelineRecord.EventType.ForcedLayout: 630 case WI.LayoutTimelineRecord.EventType.Layout: 631 return WI.CPUTimelineView.SampleType.Layout; 632 case WI.LayoutTimelineRecord.EventType.Paint: 633 case WI.LayoutTimelineRecord.EventType.Composite: 634 return WI.CPUTimelineView.SampleType.Paint; 635 } 636 }); 637 638 let samplesIdle = 0; 639 let samplesScript = 0; 640 let samplesLayout = 0; 641 let samplesPaint = 0; 642 let samplesStyle = 0; 643 for (let i = 0; i < samples.length; ++i) { 644 switch (samples[i]) { 645 case undefined: 646 samplesIdle++; 647 break; 648 case WI.CPUTimelineView.SampleType.Script: 649 samplesScript++; 650 break; 651 case WI.CPUTimelineView.SampleType.Layout: 652 samplesLayout++; 653 break; 654 case WI.CPUTimelineView.SampleType.Paint: 655 samplesPaint++; 656 break; 657 case WI.CPUTimelineView.SampleType.Style: 658 samplesStyle++; 659 break; 660 } 661 } 662 663 return { 664 samples, 665 samplesIdle, 666 samplesScript, 667 samplesLayout, 668 samplesPaint, 669 samplesStyle, 670 }; 671 } 672 673 _removeWorkerThreadViews() 674 { 675 if (!this._workerViews.length) 676 return; 677 678 for (let view of this._workerViews) 679 this.removeSubview(view); 680 681 this._workerViews = []; 682 } 683 684 _clearBreakdownLegend() 685 { 686 this._breakdownLegendScriptElement.textContent = emDash; 687 this._breakdownLegendLayoutElement.textContent = emDash; 688 this._breakdownLegendPaintElement.textContent = emDash; 689 this._breakdownLegendStyleElement.textContent = emDash; 690 691 this._breakdownChart.centerElement.removeChildren(); 692 } 189 693 190 694 _cpuTimelineRecordAdded(event) … … 197 701 } 198 702 }; 703 704 // NOTE: UI follows this order. 705 WI.CPUTimelineView.SampleType = { 706 Script: "sample-type-script", 707 Layout: "sample-type-layout", 708 Paint: "sample-type-paint", 709 Style: "sample-type-style", 710 }; -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.css
r242072 r242073 24 24 */ 25 25 26 .cpu-usage- view {26 .cpu-usage-stacked-view { 27 27 display: flex; 28 28 width: 100%; 29 height: 76px; /* Keep this in sync with cpuUsageViewHeight + 1 (for border-bottom)*/29 height: calc(var(--cpu-usage-stacked-view-height) + 1px); /* +1 for border-bottom */ 30 30 border-bottom: 1px solid var(--border-color); 31 31 } 32 32 33 .cpu-usage- view > .details {33 .cpu-usage-stacked-view > .details { 34 34 flex-shrink: 0; 35 35 width: 150px; 36 36 padding-top: 10px; 37 -webkit-padding-start: 15px; 37 38 font-family: -webkit-system-font, sans-serif; 38 39 font-size: 12px; 39 40 color: var(--text-color-secondary); 40 -webkit-padding-start: 15px;41 42 - -cpu-usage-view-details-border-end: 1px solid var(--border-color);41 overflow: hidden; 42 text-overflow: ellipsis; 43 -webkit-border-end: 1px solid var(--border-color); 43 44 } 44 45 45 body[dir=ltr] .cpu-usage-view > .details { 46 border-right: var(--cpu-usage-view-details-border-end); 46 .cpu-usage-stacked-view > .details > .name { 47 color: var(--text-color); 48 white-space: nowrap; 47 49 } 48 50 49 body[dir=rtl] .cpu-usage-view > .details { 50 border-left: var(--cpu-usage-view-details-border-end); 51 } 52 53 body[dir=rtl] .cpu-usage-view > .graph { 51 body[dir=rtl] .cpu-usage-stacked-view > .graph { 54 52 transform: scaleX(-1); 55 53 } 56 54 57 .cpu-usage-view > .graph, 58 .cpu-usage-view > .graph > .line-chart, 59 .cpu-usage-view > .graph > .line-chart > svg { 55 .cpu-usage-stacked-view > .graph { 56 position: relative; 57 } 58 59 .cpu-usage-stacked-view > .graph, 60 .cpu-usage-stacked-view > .graph > .stacked-line-chart, 61 .cpu-usage-stacked-view > .graph > .stacked-line-chart > svg { 60 62 width: 100%; 61 63 height: 100%; -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageStackedView.js
r242072 r242073 24 24 */ 25 25 26 WI.CPUUsage View = class CPUUsageView extends WI.View26 WI.CPUUsageStackedView = class CPUUsageStackedView extends WI.View 27 27 { 28 constructor( )28 constructor(displayName) 29 29 { 30 30 super(); 31 31 32 this.element.classList.add("cpu-usage- view");32 this.element.classList.add("cpu-usage-stacked-view"); 33 33 34 34 this._detailsElement = this.element.appendChild(document.createElement("div")); 35 35 this._detailsElement.classList.add("details"); 36 36 37 let detailsNameElement = this._detailsElement.appendChild(document.createElement("span")); 38 detailsNameElement.classList.add("name"); 39 detailsNameElement.textContent = displayName; 40 41 this._detailsElement.appendChild(document.createElement("br")); 37 42 this._detailsAverageElement = this._detailsElement.appendChild(document.createElement("span")); 38 43 this._detailsElement.appendChild(document.createElement("br")); … … 45 50 this._graphElement.classList.add("graph"); 46 51 47 this._chart = new WI.LineChart; 52 this._chart = new WI.StackedLineChart; 53 this._chart.initializeSections(["main-thread-usage", "worker-thread-usage", "total-usage"]); 48 54 this.addSubview(this._chart); 49 55 this._graphElement.appendChild(this._chart.element); … … 51 57 52 58 // Public 59 60 get chart() { return this._chart; } 53 61 54 62 clear() … … 60 68 61 69 this._chart.clear(); 70 this._chart.needsLayout(); 62 71 } 63 72 … … 85 94 // Extend the first data point to the start so it doesn't look like we originate at zero size. 86 95 let firstX = 0; 87 let firstY = yScale(dataPoints[0].size); 88 this._chart.addPoint(firstX, firstY); 96 let firstY1 = yScale(dataPoints[0].mainThreadUsage); 97 let firstY2 = yScale(dataPoints[0].mainThreadUsage + dataPoints[0].workerThreadUsage); 98 let firstY3 = yScale(dataPoints[0].usage); 99 this._chart.addPointSet(firstX, [firstY1, firstY2, firstY3]); 89 100 90 101 // Points for data points. 91 102 for (let dataPoint of dataPoints) { 92 103 let x = xScale(dataPoint.time); 93 let y = yScale(dataPoint.size); 94 this._chart.addPoint(x, y); 104 let y1 = yScale(dataPoint.mainThreadUsage); 105 let y2 = yScale(dataPoint.mainThreadUsage + dataPoint.workerThreadUsage); 106 let y3 = yScale(dataPoint.usage) 107 this._chart.addPointSet(x, [y1, y2, y3]); 95 108 } 96 109 … … 98 111 let lastDataPoint = dataPoints.lastValue; 99 112 let lastX = Math.floor(xScale(visibleEndTime)); 100 let lastY = yScale(lastDataPoint.size); 101 this._chart.addPoint(lastX, lastY); 113 let lastY1 = yScale(lastDataPoint.mainThreadUsage); 114 let lastY2 = yScale(lastDataPoint.mainThreadUsage + lastDataPoint.workerThreadUsage); 115 let lastY3 = yScale(lastDataPoint.usage); 116 this._chart.addPointSet(lastX, [lastY1, lastY2, lastY3]); 102 117 } 103 118 -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.css
r240763 r242073 27 27 display: flex; 28 28 width: 100%; 29 height: 76px; /* Keep this in sync with cpuUsageViewHeight + 1 (for border-bottom)*/29 height: calc(var(--cpu-usage-view-height) + 1px); /* +1 for border-bottom */ 30 30 border-bottom: 1px solid var(--border-color); 31 31 } … … 35 35 width: 150px; 36 36 padding-top: 10px; 37 -webkit-padding-start: 15px; 37 38 font-family: -webkit-system-font, sans-serif; 38 39 font-size: 12px; 39 40 color: var(--text-color-secondary); 40 -webkit-padding-start: 15px;41 42 - -cpu-usage-view-details-border-end: 1px solid var(--border-color);41 overflow: hidden; 42 text-overflow: ellipsis; 43 -webkit-border-end: 1px solid var(--border-color); 43 44 } 44 45 45 body[dir=ltr] .cpu-usage-view > .details { 46 border-right: var(--cpu-usage-view-details-border-end); 47 } 48 49 body[dir=rtl] .cpu-usage-view > .details { 50 border-left: var(--cpu-usage-view-details-border-end); 46 .cpu-usage-view > .details > .name { 47 color: var(--text-color); 48 white-space: nowrap; 51 49 } 52 50 53 51 body[dir=rtl] .cpu-usage-view > .graph { 54 52 transform: scaleX(-1); 53 } 54 55 .cpu-usage-view > .graph { 56 position: relative; 55 57 } 56 58 -
trunk/Source/WebInspectorUI/UserInterface/Views/CPUUsageView.js
r240763 r242073 26 26 WI.CPUUsageView = class CPUUsageView extends WI.View 27 27 { 28 constructor( )28 constructor(displayName) 29 29 { 30 30 super(); … … 35 35 this._detailsElement.classList.add("details"); 36 36 37 if (displayName) { 38 let detailsNameElement = this._detailsElement.appendChild(document.createElement("span")); 39 detailsNameElement.classList.add("name"); 40 detailsNameElement.textContent = displayName; 41 this._detailsElement.appendChild(document.createElement("br")); 42 } 43 37 44 this._detailsAverageElement = this._detailsElement.appendChild(document.createElement("span")); 38 45 this._detailsElement.appendChild(document.createElement("br")); 39 46 this._detailsMaxElement = this._detailsElement.appendChild(document.createElement("span")); 40 this._detailsElement.appendChild(document.createElement("br"));41 this._detailsMinElement = this._detailsElement.appendChild(document.createElement("span"));42 47 this._updateDetails(NaN, NaN); 43 48 … … 52 57 // Public 53 58 59 get chart() { return this._chart; } 60 54 61 clear() 55 62 { 56 63 this._cachedAverageSize = undefined; 57 this._cachedMinSize = undefined;58 64 this._cachedMaxSize = undefined; 59 65 this._updateDetails(NaN, NaN); … … 62 68 } 63 69 64 updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale )70 updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale, property) 65 71 { 66 72 console.assert(size instanceof WI.Size); … … 69 75 console.assert(min <= max); 70 76 console.assert(min <= average && average <= max); 77 console.assert(property, "CPUUsageView needs a property of the dataPoints to graph"); 71 78 72 79 this._updateDetails(min, max, average); … … 85 92 // Extend the first data point to the start so it doesn't look like we originate at zero size. 86 93 let firstX = 0; 87 let firstY = yScale(dataPoints[0] .size);94 let firstY = yScale(dataPoints[0][property]); 88 95 this._chart.addPoint(firstX, firstY); 89 96 … … 91 98 for (let dataPoint of dataPoints) { 92 99 let x = xScale(dataPoint.time); 93 let y = yScale(dataPoint .size);100 let y = yScale(dataPoint[property]); 94 101 this._chart.addPoint(x, y); 95 102 } … … 98 105 let lastDataPoint = dataPoints.lastValue; 99 106 let lastX = Math.floor(xScale(visibleEndTime)); 100 let lastY = yScale(lastDataPoint .size);107 let lastY = yScale(lastDataPoint[property]); 101 108 this._chart.addPoint(lastX, lastY); 102 109 } … … 106 113 _updateDetails(minSize, maxSize, averageSize) 107 114 { 108 if (this._cachedM inSize === minSize && this._cachedMaxSize === maxSize && this._cachedAverageSize === averageSize)115 if (this._cachedMaxSize === maxSize && this._cachedAverageSize === averageSize) 109 116 return; 110 117 111 118 this._cachedAverageSize = averageSize; 112 this._cachedMinSize = minSize;113 119 this._cachedMaxSize = maxSize; 114 120 115 this._detailsAverageElement.textContent = WI.UIString("Average: %s").format(Number.isFinite(maxSize) ? Number.percentageString(averageSize / 100) : emDash); 121 this._detailsAverageElement.hidden = !Number.isFinite(averageSize); 122 this._detailsMaxElement.hidden = !Number.isFinite(maxSize); 123 124 this._detailsAverageElement.textContent = WI.UIString("Average: %s").format(Number.isFinite(averageSize) ? Number.percentageString(averageSize / 100) : emDash); 116 125 this._detailsMaxElement.textContent = WI.UIString("Highest: %s").format(Number.isFinite(maxSize) ? Number.percentageString(maxSize / 100) : emDash); 117 this._detailsMinElement.textContent = WI.UIString("Lowest: %s").format(Number.isFinite(minSize) ? Number.percentageString(minSize / 100) : emDash);118 126 } 119 127 }; -
trunk/Source/WebInspectorUI/UserInterface/Views/LegacyCPUTimelineView.css
r241325 r242073 66 66 .timeline-view.legacy-cpu .cpu-usage-view .line-chart > svg > path { 67 67 stroke: var(--cpu-stroke-color); 68 fill: var(--cpu- fill-color);68 fill: var(--cpu-main-thread-fill-color); 69 69 } -
trunk/Source/WebInspectorUI/UserInterface/Views/LegacyCPUTimelineView.js
r241325 r242073 58 58 } 59 59 60 // Static 61 62 static get cpuUsageViewHeight() { return 150; } 63 60 64 // Public 61 65 … … 95 99 96 100 get showsFilterBar() { return false; } 101 102 initialLayout() 103 { 104 this.element.style.setProperty("--cpu-usage-view-height", LegacyCPUTimelineView.cpuUsageViewHeight + "px"); 105 } 97 106 98 107 layout() … … 105 114 this._timelineRuler.startTime = this.startTime; 106 115 this._timelineRuler.endTime = this.endTime; 107 108 const cpuUsageViewHeight = 75; // Keep this in sync with .legacy-cpu-usage-view109 116 110 117 let graphStartTime = this.startTime; … … 169 176 } 170 177 171 let size = new WI.Size(xScale(graphEndTime), cpuUsageViewHeight);178 let size = new WI.Size(xScale(graphEndTime), LegacyCPUTimelineView.cpuUsageViewHeight); 172 179 173 180 function yScale(value) { … … 175 182 } 176 183 177 view.updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale );184 view.updateChart(dataPoints, size, visibleEndTime, min, max, average, xScale, yScale, "size"); 178 185 } 179 186 -
trunk/Source/WebInspectorUI/UserInterface/Views/MemoryCategoryView.css
r240763 r242073 35 35 width: 150px; 36 36 padding-top: 10px; 37 -webkit-padding-start: 15px; 37 38 font-family: -webkit-system-font, sans-serif; 38 39 font-size: 12px; 39 40 color: var(--text-color-secondary); 40 -webkit-padding-start: 15px; 41 42 --memory-category-view-details-border-end: 1px solid var(--border-color); 43 } 44 45 body[dir=ltr] .memory-category-view > .details { 46 border-right: var(--memory-category-view-details-border-end); 47 } 48 49 body[dir=rtl] .memory-category-view > .details { 50 border-left: var(--memory-category-view-details-border-end); 41 overflow: hidden; 42 text-overflow: ellipsis; 43 -webkit-border-end: 1px solid var(--border-color); 51 44 } 52 45 53 46 .memory-category-view > .details > .name { 54 47 color: var(--text-color); 48 white-space: nowrap; 55 49 } 56 57 50 58 51 body[dir=rtl] .memory-category-view > .graph { -
trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineOverviewGraph.js
r240871 r242073 211 211 } 212 212 } 213 214 this._chart.updateLayout();215 213 } 216 214 -
trunk/Source/WebInspectorUI/UserInterface/Views/MemoryTimelineView.css
r240763 r242073 24 24 */ 25 25 26 .timeline-view.memory {26 body .timeline-view.memory { 27 27 overflow: scroll; 28 28 } -
trunk/Source/WebInspectorUI/UserInterface/Views/Variables.css
r242072 r242073 130 130 131 131 --cpu-stroke-color: hsl(118, 33%, 42%); 132 --cpu-fill-color: hsl(118, 43%, 55%); 132 --cpu-fill-color: hsl(81, 80%, 50%); 133 --cpu-main-thread-fill-color: hsl(118, 43%, 55%); 134 --cpu-worker-thread-fill-color: hsl(45, 94.75%, 55%); 135 136 --cpu-idle-fill-color: hsl(220, 10%, 75%); 137 --cpu-idle-stroke-color: hsl(220, 10%, 55%); 138 --cpu-script-fill-color: hsl(269, 65%, 75%); 139 --cpu-script-stroke-color: hsl(269, 33%, 50%); 140 --cpu-style-fill-color: hsl(22, 60%, 70%); 141 --cpu-style-stroke-color: hsl(22, 40%, 50%); 142 --cpu-layout-fill-color: hsl(0, 65%, 75%); 143 --cpu-layout-stroke-color: hsl(0, 54%, 50%); 144 --cpu-paint-fill-color: hsl(76, 49%, 75%); 145 --cpu-paint-stroke-color: hsl(79, 45%, 50%); 133 146 134 147 --network-header-color: hsl(204, 52%, 55%); -
trunk/Source/WebInspectorUI/UserInterface/Views/View.js
r237720 r242073 118 118 { 119 119 console.assert(view instanceof WI.View); 120 console.assert( view.element.parentNode === this._element, "Subview DOM element must be a child of the parent view element.");120 console.assert(this._element.contains(view.element), "Subview DOM element must be a child of the parent view element."); 121 121 122 122 let index = this._subviews.lastIndexOf(view);
Note:
See TracChangeset
for help on using the changeset viewer.