| OLD | NEW |
| 1 <!DOCTYPE html> | 1 <!DOCTYPE html> |
| 2 <!-- | 2 <!-- |
| 3 Copyright (c) 2013 The Chromium Authors. All rights reserved. | 3 Copyright (c) 2013 The Chromium Authors. All rights reserved. |
| 4 Use of this source code is governed by a BSD-style license that can be | 4 Use of this source code is governed by a BSD-style license that can be |
| 5 found in the LICENSE file. | 5 found in the LICENSE file. |
| 6 --> | 6 --> |
| 7 | 7 |
| 8 <link rel="import" href="/tracing/model/async_slice.html"> | 8 <link rel="import" href="/tracing/model/async_slice.html"> |
| 9 <link rel="import" href="/tracing/model/event_set.html"> | 9 <link rel="import" href="/tracing/model/event_set.html"> |
| 10 <link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> | 10 <link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> |
| (...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 58 TOUCH_END: 'TouchEnd', | 58 TOUCH_END: 'TouchEnd', |
| 59 TOUCH_MOVE: 'TouchMove', | 59 TOUCH_MOVE: 'TouchMove', |
| 60 TOUCH_START: 'TouchStart', | 60 TOUCH_START: 'TouchStart', |
| 61 UNKNOWN: 'UNKNOWN' | 61 UNKNOWN: 'UNKNOWN' |
| 62 }; | 62 }; |
| 63 | 63 |
| 64 function InputLatencyAsyncSlice() { | 64 function InputLatencyAsyncSlice() { |
| 65 AsyncSlice.apply(this, arguments); | 65 AsyncSlice.apply(this, arguments); |
| 66 this.associatedEvents_ = new EventSet(); | 66 this.associatedEvents_ = new EventSet(); |
| 67 this.typeName_ = undefined; | 67 this.typeName_ = undefined; |
| 68 if (!this.isLegacyEvent) | 68 if (!this.isLegacyEvent) { |
| 69 this.determineModernTypeName_(); | 69 this.determineModernTypeName_(); |
| 70 } |
| 70 } | 71 } |
| 71 | 72 |
| 72 InputLatencyAsyncSlice.prototype = { | 73 InputLatencyAsyncSlice.prototype = { |
| 73 __proto__: AsyncSlice.prototype, | 74 __proto__: AsyncSlice.prototype, |
| 74 | 75 |
| 75 // Legacy InputLatencyAsyncSlices involve a top-level slice titled | 76 // Legacy InputLatencyAsyncSlices involve a top-level slice titled |
| 76 // "InputLatency" containing a subSlice whose title starts with | 77 // "InputLatency" containing a subSlice whose title starts with |
| 77 // "InputLatency:". Modern InputLatencyAsyncSlices involve a single | 78 // "InputLatency:". Modern InputLatencyAsyncSlices involve a single |
| 78 // top-level slice whose title starts with "InputLatency::". | 79 // top-level slice whose title starts with "InputLatency::". |
| 79 // Legacy subSlices are not available at construction time, so | 80 // Legacy subSlices are not available at construction time, so |
| 80 // determineLegacyTypeName_() must be called at get time. | 81 // determineLegacyTypeName_() must be called at get time. |
| 81 // So this returns false for the legacy subSlice events titled like | 82 // So this returns false for the legacy subSlice events titled like |
| 82 // "InputLatency:Foo" even though they are technically legacy events. | 83 // "InputLatency:Foo" even though they are technically legacy events. |
| 83 get isLegacyEvent() { | 84 get isLegacyEvent() { |
| 84 return this.title === 'InputLatency'; | 85 return this.title === 'InputLatency'; |
| 85 }, | 86 }, |
| 86 | 87 |
| 87 get typeName() { | 88 get typeName() { |
| 88 if (!this.typeName_) | 89 if (!this.typeName_) { |
| 89 this.determineLegacyTypeName_(); | 90 this.determineLegacyTypeName_(); |
| 91 } |
| 90 return this.typeName_; | 92 return this.typeName_; |
| 91 }, | 93 }, |
| 92 | 94 |
| 93 checkTypeName_: function() { | 95 checkTypeName_: function() { |
| 94 if (!this.typeName_) | 96 if (!this.typeName_) { |
| 95 throw new Error('Unable to determine typeName'); | 97 throw new Error('Unable to determine typeName'); |
| 98 } |
| 96 var found = false; | 99 var found = false; |
| 97 for (var typeName in INPUT_EVENT_TYPE_NAMES) { | 100 for (var typeName in INPUT_EVENT_TYPE_NAMES) { |
| 98 if (this.typeName === INPUT_EVENT_TYPE_NAMES[typeName]) { | 101 if (this.typeName === INPUT_EVENT_TYPE_NAMES[typeName]) { |
| 99 found = true; | 102 found = true; |
| 100 break; | 103 break; |
| 101 } | 104 } |
| 102 } | 105 } |
| 103 if (!found) | 106 if (!found) { |
| 104 this.typeName_ = INPUT_EVENT_TYPE_NAMES.UNKNOWN; | 107 this.typeName_ = INPUT_EVENT_TYPE_NAMES.UNKNOWN; |
| 108 } |
| 105 }, | 109 }, |
| 106 | 110 |
| 107 determineModernTypeName_: function() { | 111 determineModernTypeName_: function() { |
| 108 // This method works both on modern events titled like | 112 // This method works both on modern events titled like |
| 109 // "InputLatency::Foo" and also on the legacy subSlices titled like | 113 // "InputLatency::Foo" and also on the legacy subSlices titled like |
| 110 // "InputLatency:Foo". Modern events' titles contain 2 colons, whereas the | 114 // "InputLatency:Foo". Modern events' titles contain 2 colons, whereas the |
| 111 // legacy subSlices events contain 1 colon. | 115 // legacy subSlices events contain 1 colon. |
| 112 | 116 |
| 113 var lastColonIndex = this.title.lastIndexOf(':'); | 117 var lastColonIndex = this.title.lastIndexOf(':'); |
| 114 if (lastColonIndex < 0) | 118 if (lastColonIndex < 0) return; |
| 115 return; | |
| 116 | 119 |
| 117 var characterAfterLastColonIndex = lastColonIndex + 1; | 120 var characterAfterLastColonIndex = lastColonIndex + 1; |
| 118 this.typeName_ = this.title.slice(characterAfterLastColonIndex); | 121 this.typeName_ = this.title.slice(characterAfterLastColonIndex); |
| 119 | 122 |
| 120 // Check that the determined typeName is known. | 123 // Check that the determined typeName is known. |
| 121 this.checkTypeName_(); | 124 this.checkTypeName_(); |
| 122 }, | 125 }, |
| 123 | 126 |
| 124 determineLegacyTypeName_: function() { | 127 determineLegacyTypeName_: function() { |
| 125 // Iterate over all descendent subSlices. | 128 // Iterate over all descendent subSlices. |
| 126 for (var subSlice of this.enumerateAllDescendents()) { | 129 for (var subSlice of this.enumerateAllDescendents()) { |
| 127 // If |subSlice| is not an InputLatencyAsyncSlice, then ignore it. | 130 // If |subSlice| is not an InputLatencyAsyncSlice, then ignore it. |
| 128 var subSliceIsAInputLatencyAsyncSlice = ( | 131 var subSliceIsAInputLatencyAsyncSlice = ( |
| 129 subSlice instanceof InputLatencyAsyncSlice); | 132 subSlice instanceof InputLatencyAsyncSlice); |
| 130 if (!subSliceIsAInputLatencyAsyncSlice) | 133 if (!subSliceIsAInputLatencyAsyncSlice) continue; |
| 131 continue; | |
| 132 | 134 |
| 133 // If |subSlice| does not have a typeName, then ignore it. | 135 // If |subSlice| does not have a typeName, then ignore it. |
| 134 if (!subSlice.typeName) | 136 if (!subSlice.typeName) continue; |
| 135 continue; | |
| 136 | 137 |
| 137 // If |this| already has a typeName and |subSlice| has a different | 138 // If |this| already has a typeName and |subSlice| has a different |
| 138 // typeName, then explode! | 139 // typeName, then explode! |
| 139 if (this.typeName_ && subSlice.typeName_) { | 140 if (this.typeName_ && subSlice.typeName_) { |
| 140 var subSliceHasDifferentTypeName = ( | 141 var subSliceHasDifferentTypeName = ( |
| 141 this.typeName_ !== subSlice.typeName_); | 142 this.typeName_ !== subSlice.typeName_); |
| 142 if (subSliceHasDifferentTypeName) { | 143 if (subSliceHasDifferentTypeName) { |
| 143 throw new Error( | 144 throw new Error( |
| 144 'InputLatencyAsyncSlice.determineLegacyTypeName_() ' + | 145 'InputLatencyAsyncSlice.determineLegacyTypeName_() ' + |
| 145 ' found multiple typeNames'); | 146 ' found multiple typeNames'); |
| (...skipping 12 matching lines...) Expand all Loading... |
| 158 } | 159 } |
| 159 | 160 |
| 160 // Check that the determined typeName is known. | 161 // Check that the determined typeName is known. |
| 161 this.checkTypeName_(); | 162 this.checkTypeName_(); |
| 162 }, | 163 }, |
| 163 | 164 |
| 164 getRendererHelper: function(sourceSlices) { | 165 getRendererHelper: function(sourceSlices) { |
| 165 var traceModel = this.startThread.parent.model; | 166 var traceModel = this.startThread.parent.model; |
| 166 var modelHelper = traceModel.getOrCreateHelper( | 167 var modelHelper = traceModel.getOrCreateHelper( |
| 167 tr.model.helpers.ChromeModelHelper); | 168 tr.model.helpers.ChromeModelHelper); |
| 168 if (!modelHelper) | 169 if (!modelHelper) return undefined; |
| 169 return undefined; | |
| 170 | 170 |
| 171 var mainThread = undefined; | 171 var mainThread = undefined; |
| 172 var compositorThread = undefined; | 172 var compositorThread = undefined; |
| 173 | 173 |
| 174 for (var i in sourceSlices) { | 174 for (var i in sourceSlices) { |
| 175 if (sourceSlices[i].parentContainer.name === | 175 if (sourceSlices[i].parentContainer.name === |
| 176 MAIN_RENDERER_THREAD_NAME) | 176 MAIN_RENDERER_THREAD_NAME) { |
| 177 mainThread = sourceSlices[i].parentContainer; | 177 mainThread = sourceSlices[i].parentContainer; |
| 178 else if (sourceSlices[i].parentContainer.name === | 178 } else if (sourceSlices[i].parentContainer.name === |
| 179 COMPOSITOR_THREAD_NAME) | 179 COMPOSITOR_THREAD_NAME) { |
| 180 compositorThread = sourceSlices[i].parentContainer; | 180 compositorThread = sourceSlices[i].parentContainer; |
| 181 } |
| 181 | 182 |
| 182 if (mainThread && compositorThread) | 183 if (mainThread && compositorThread) break; |
| 183 break; | |
| 184 } | 184 } |
| 185 | 185 |
| 186 var rendererHelpers = modelHelper.rendererHelpers; | 186 var rendererHelpers = modelHelper.rendererHelpers; |
| 187 | 187 |
| 188 var pids = Object.keys(rendererHelpers); | 188 var pids = Object.keys(rendererHelpers); |
| 189 for (var i = 0; i < pids.length; i++) { | 189 for (var i = 0; i < pids.length; i++) { |
| 190 var pid = pids[i]; | 190 var pid = pids[i]; |
| 191 var rendererHelper = rendererHelpers[pid]; | 191 var rendererHelper = rendererHelpers[pid]; |
| 192 if (rendererHelper.mainThread === mainThread || | 192 if (rendererHelper.mainThread === mainThread || |
| 193 rendererHelper.compositorThread === compositorThread) | 193 rendererHelper.compositorThread === compositorThread) { |
| 194 return rendererHelper; | 194 return rendererHelper; |
| 195 } |
| 195 } | 196 } |
| 196 | 197 |
| 197 return undefined; | 198 return undefined; |
| 198 }, | 199 }, |
| 199 | 200 |
| 200 addEntireSliceHierarchy: function(slice) { | 201 addEntireSliceHierarchy: function(slice) { |
| 201 this.associatedEvents_.push(slice); | 202 this.associatedEvents_.push(slice); |
| 202 slice.iterateAllSubsequentSlices(function(subsequentSlice) { | 203 slice.iterateAllSubsequentSlices(function(subsequentSlice) { |
| 203 this.associatedEvents_.push(subsequentSlice); | 204 this.associatedEvents_.push(subsequentSlice); |
| 204 }, this); | 205 }, this); |
| 205 }, | 206 }, |
| 206 | 207 |
| 207 addDirectlyAssociatedEvents: function(flowEvents) { | 208 addDirectlyAssociatedEvents: function(flowEvents) { |
| 208 var slices = []; | 209 var slices = []; |
| 209 | 210 |
| 210 flowEvents.forEach(function(flowEvent) { | 211 flowEvents.forEach(function(flowEvent) { |
| 211 this.associatedEvents_.push(flowEvent); | 212 this.associatedEvents_.push(flowEvent); |
| 212 var newSource = flowEvent.startSlice.mostTopLevelSlice; | 213 var newSource = flowEvent.startSlice.mostTopLevelSlice; |
| 213 if (slices.indexOf(newSource) === -1) | 214 if (slices.indexOf(newSource) === -1) { |
| 214 slices.push(newSource); | 215 slices.push(newSource); |
| 216 } |
| 215 }, this); | 217 }, this); |
| 216 | 218 |
| 217 var lastFlowEvent = flowEvents[flowEvents.length - 1]; | 219 var lastFlowEvent = flowEvents[flowEvents.length - 1]; |
| 218 var lastSource = lastFlowEvent.endSlice.mostTopLevelSlice; | 220 var lastSource = lastFlowEvent.endSlice.mostTopLevelSlice; |
| 219 if (slices.indexOf(lastSource) === -1) | 221 if (slices.indexOf(lastSource) === -1) { |
| 220 slices.push(lastSource); | 222 slices.push(lastSource); |
| 223 } |
| 221 | 224 |
| 222 return slices; | 225 return slices; |
| 223 }, | 226 }, |
| 224 | 227 |
| 225 // Find the Latency::ScrollUpdate slice that corresponds to the | 228 // Find the Latency::ScrollUpdate slice that corresponds to the |
| 226 // InputLatency::GestureScrollUpdate slice. | 229 // InputLatency::GestureScrollUpdate slice. |
| 227 // The C++ CL that makes this connection is at: | 230 // The C++ CL that makes this connection is at: |
| 228 // https://codereview.chromium.org/1178963003 | 231 // https://codereview.chromium.org/1178963003 |
| 229 addScrollUpdateEvents: function(rendererHelper) { | 232 addScrollUpdateEvents: function(rendererHelper) { |
| 230 if (!rendererHelper || !rendererHelper.compositorThread) | 233 if (!rendererHelper || !rendererHelper.compositorThread) { |
| 231 return; | 234 return; |
| 235 } |
| 232 | 236 |
| 233 var compositorThread = rendererHelper.compositorThread; | 237 var compositorThread = rendererHelper.compositorThread; |
| 234 var gestureScrollUpdateStart = this.start; | 238 var gestureScrollUpdateStart = this.start; |
| 235 var gestureScrollUpdateEnd = this.end; | 239 var gestureScrollUpdateEnd = this.end; |
| 236 | 240 |
| 237 var allCompositorAsyncSlices = | 241 var allCompositorAsyncSlices = |
| 238 compositorThread.asyncSliceGroup.slices; | 242 compositorThread.asyncSliceGroup.slices; |
| 239 | 243 |
| 240 for (var i in allCompositorAsyncSlices) { | 244 for (var i in allCompositorAsyncSlices) { |
| 241 var slice = allCompositorAsyncSlices[i]; | 245 var slice = allCompositorAsyncSlices[i]; |
| 242 | 246 |
| 243 if (slice.title !== 'Latency::ScrollUpdate') | 247 if (slice.title !== 'Latency::ScrollUpdate') continue; |
| 244 continue; | |
| 245 | 248 |
| 246 var parentId = slice.args.data. | 249 var parentId = slice.args.data. |
| 247 INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT. | 250 INPUT_EVENT_LATENCY_FORWARD_SCROLL_UPDATE_TO_MAIN_COMPONENT. |
| 248 sequence_number; | 251 sequence_number; |
| 249 | 252 |
| 250 if (parentId === undefined) { | 253 if (parentId === undefined) { |
| 251 // Old trace, we can only rely on the timestamp to find the slice | 254 // Old trace, we can only rely on the timestamp to find the slice |
| 252 if (slice.start < gestureScrollUpdateStart || | 255 if (slice.start < gestureScrollUpdateStart || |
| 253 slice.start >= gestureScrollUpdateEnd) | 256 slice.start >= gestureScrollUpdateEnd) { |
| 254 continue; | 257 continue; |
| 258 } |
| 255 } else { | 259 } else { |
| 256 // New trace, we can definitively find the latency slice by comparing | 260 // New trace, we can definitively find the latency slice by comparing |
| 257 // its sequence number with gesture id | 261 // its sequence number with gesture id |
| 258 if (parseInt(parentId) !== parseInt(this.id)) | 262 if (parseInt(parentId) !== parseInt(this.id)) { |
| 259 continue; | 263 continue; |
| 264 } |
| 260 } | 265 } |
| 261 | 266 |
| 262 slice.associatedEvents.forEach(function(event) { | 267 slice.associatedEvents.forEach(function(event) { |
| 263 this.associatedEvents_.push(event); | 268 this.associatedEvents_.push(event); |
| 264 }, this); | 269 }, this); |
| 265 break; | 270 break; |
| 266 } | 271 } |
| 267 }, | 272 }, |
| 268 | 273 |
| 269 // Return true if the slice hierarchy is tracked by LatencyInfo of other | 274 // Return true if the slice hierarchy is tracked by LatencyInfo of other |
| 270 // input latency events. If the slice hierarchy is tracked by both, this | 275 // input latency events. If the slice hierarchy is tracked by both, this |
| 271 // function still returns true. | 276 // function still returns true. |
| 272 belongToOtherInputs: function(slice, flowEvents) { | 277 belongToOtherInputs: function(slice, flowEvents) { |
| 273 var fromOtherInputs = false; | 278 var fromOtherInputs = false; |
| 274 | 279 |
| 275 slice.iterateEntireHierarchy(function(subsequentSlice) { | 280 slice.iterateEntireHierarchy(function(subsequentSlice) { |
| 276 if (fromOtherInputs) | 281 if (fromOtherInputs) return; |
| 277 return; | |
| 278 | 282 |
| 279 subsequentSlice.inFlowEvents.forEach(function(inflow) { | 283 subsequentSlice.inFlowEvents.forEach(function(inflow) { |
| 280 if (fromOtherInputs) | 284 if (fromOtherInputs) return; |
| 281 return; | |
| 282 | 285 |
| 283 if (inflow.category.indexOf('input') > -1) { | 286 if (inflow.category.indexOf('input') > -1) { |
| 284 if (flowEvents.indexOf(inflow) === -1) | 287 if (flowEvents.indexOf(inflow) === -1) { |
| 285 fromOtherInputs = true; | 288 fromOtherInputs = true; |
| 289 } |
| 286 } | 290 } |
| 287 }, this); | 291 }, this); |
| 288 }, this); | 292 }, this); |
| 289 | 293 |
| 290 return fromOtherInputs; | 294 return fromOtherInputs; |
| 291 }, | 295 }, |
| 292 | 296 |
| 293 // Return true if |event| triggers slices of other inputs. | 297 // Return true if |event| triggers slices of other inputs. |
| 294 triggerOtherInputs: function(event, flowEvents) { | 298 triggerOtherInputs: function(event, flowEvents) { |
| 295 if (event.outFlowEvents === undefined || | 299 if (event.outFlowEvents === undefined || |
| 296 event.outFlowEvents.length === 0) | 300 event.outFlowEvents.length === 0) { |
| 297 return false; | 301 return false; |
| 302 } |
| 298 | 303 |
| 299 // Once we fix the bug of flow event binding, there should exist one and | 304 // Once we fix the bug of flow event binding, there should exist one and |
| 300 // only one outgoing flow (PostTask) from ScheduleBeginImplFrameDeadline | 305 // only one outgoing flow (PostTask) from ScheduleBeginImplFrameDeadline |
| 301 // and PostComposite. | 306 // and PostComposite. |
| 302 var flow = event.outFlowEvents[0]; | 307 var flow = event.outFlowEvents[0]; |
| 303 | 308 |
| 304 if (flow.category !== POSTTASK_FLOW_EVENT || | 309 if (flow.category !== POSTTASK_FLOW_EVENT || |
| 305 !flow.endSlice) | 310 !flow.endSlice) { |
| 306 return false; | 311 return false; |
| 312 } |
| 307 | 313 |
| 308 var endSlice = flow.endSlice; | 314 var endSlice = flow.endSlice; |
| 309 if (this.belongToOtherInputs(endSlice.mostTopLevelSlice, flowEvents)) | 315 if (this.belongToOtherInputs(endSlice.mostTopLevelSlice, flowEvents)) { |
| 310 return true; | 316 return true; |
| 317 } |
| 311 | 318 |
| 312 return false; | 319 return false; |
| 313 }, | 320 }, |
| 314 | 321 |
| 315 // Follow outgoing flow of subsequentSlices in the current hierarchy. | 322 // Follow outgoing flow of subsequentSlices in the current hierarchy. |
| 316 // We also handle cases where different inputs interfere with each other. | 323 // We also handle cases where different inputs interfere with each other. |
| 317 followSubsequentSlices: function(event, queue, visited, flowEvents) { | 324 followSubsequentSlices: function(event, queue, visited, flowEvents) { |
| 318 var stopFollowing = false; | 325 var stopFollowing = false; |
| 319 var inputAck = false; | 326 var inputAck = false; |
| 320 | 327 |
| 321 event.iterateAllSubsequentSlices(function(slice) { | 328 event.iterateAllSubsequentSlices(function(slice) { |
| 322 if (stopFollowing) | 329 if (stopFollowing) return; |
| 323 return; | |
| 324 | 330 |
| 325 // Do not follow TaskQueueManager::RunTask because it causes | 331 // Do not follow TaskQueueManager::RunTask because it causes |
| 326 // many false events to be included. | 332 // many false events to be included. |
| 327 if (slice.title === 'TaskQueueManager::RunTask') | 333 if (slice.title === 'TaskQueueManager::RunTask') return; |
| 328 return; | |
| 329 | 334 |
| 330 // Do not follow ScheduledActionSendBeginMainFrame because the real | 335 // Do not follow ScheduledActionSendBeginMainFrame because the real |
| 331 // main thread BeginMainFrame is already traced by LatencyInfo flow. | 336 // main thread BeginMainFrame is already traced by LatencyInfo flow. |
| 332 if (slice.title === 'ThreadProxy::ScheduledActionSendBeginMainFrame') | 337 if (slice.title === 'ThreadProxy::ScheduledActionSendBeginMainFrame') { |
| 333 return; | 338 return; |
| 339 } |
| 334 | 340 |
| 335 // Do not follow ScheduleBeginImplFrameDeadline that triggers an | 341 // Do not follow ScheduleBeginImplFrameDeadline that triggers an |
| 336 // OnBeginImplFrameDeadline that is tracked by another LatencyInfo. | 342 // OnBeginImplFrameDeadline that is tracked by another LatencyInfo. |
| 337 if (slice.title === 'Scheduler::ScheduleBeginImplFrameDeadline') { | 343 if (slice.title === 'Scheduler::ScheduleBeginImplFrameDeadline') { |
| 338 if (this.triggerOtherInputs(slice, flowEvents)) | 344 if (this.triggerOtherInputs(slice, flowEvents)) return; |
| 339 return; | |
| 340 } | 345 } |
| 341 | 346 |
| 342 // Do not follow PostComposite that triggers CompositeImmediately | 347 // Do not follow PostComposite that triggers CompositeImmediately |
| 343 // that is tracked by another LatencyInfo. | 348 // that is tracked by another LatencyInfo. |
| 344 if (slice.title === 'CompositorImpl::PostComposite') { | 349 if (slice.title === 'CompositorImpl::PostComposite') { |
| 345 if (this.triggerOtherInputs(slice, flowEvents)) | 350 if (this.triggerOtherInputs(slice, flowEvents)) return; |
| 346 return; | |
| 347 } | 351 } |
| 348 | 352 |
| 349 // Stop following the rest of the current slice hierarchy if | 353 // Stop following the rest of the current slice hierarchy if |
| 350 // FilterAndSendWebInputEvent occurs after ProcessInputEventAck. | 354 // FilterAndSendWebInputEvent occurs after ProcessInputEventAck. |
| 351 if (slice.title === 'InputRouterImpl::ProcessInputEventAck') | 355 if (slice.title === 'InputRouterImpl::ProcessInputEventAck') { |
| 352 inputAck = true; | 356 inputAck = true; |
| 357 } |
| 353 if (inputAck && | 358 if (inputAck && |
| 354 slice.title === 'InputRouterImpl::FilterAndSendWebInputEvent') | 359 slice.title === 'InputRouterImpl::FilterAndSendWebInputEvent') { |
| 355 stopFollowing = true; | 360 stopFollowing = true; |
| 361 } |
| 356 | 362 |
| 357 this.followCurrentSlice(slice, queue, visited); | 363 this.followCurrentSlice(slice, queue, visited); |
| 358 }, this); | 364 }, this); |
| 359 }, | 365 }, |
| 360 | 366 |
| 361 // Follow outgoing flow events of the current slice. | 367 // Follow outgoing flow events of the current slice. |
| 362 followCurrentSlice: function(event, queue, visited) { | 368 followCurrentSlice: function(event, queue, visited) { |
| 363 event.outFlowEvents.forEach(function(outflow) { | 369 event.outFlowEvents.forEach(function(outflow) { |
| 364 if ((outflow.category === POSTTASK_FLOW_EVENT || | 370 if ((outflow.category === POSTTASK_FLOW_EVENT || |
| 365 outflow.category === IPC_FLOW_EVENT) && | 371 outflow.category === IPC_FLOW_EVENT) && |
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 400 }, | 406 }, |
| 401 | 407 |
| 402 sortRasterizerSlices: function(rasterWorkerThreads, | 408 sortRasterizerSlices: function(rasterWorkerThreads, |
| 403 sortedRasterizerSlices) { | 409 sortedRasterizerSlices) { |
| 404 rasterWorkerThreads.forEach(function(rasterizer) { | 410 rasterWorkerThreads.forEach(function(rasterizer) { |
| 405 Array.prototype.push.apply(sortedRasterizerSlices, | 411 Array.prototype.push.apply(sortedRasterizerSlices, |
| 406 rasterizer.sliceGroup.slices); | 412 rasterizer.sliceGroup.slices); |
| 407 }, this); | 413 }, this); |
| 408 | 414 |
| 409 sortedRasterizerSlices.sort(function(a, b) { | 415 sortedRasterizerSlices.sort(function(a, b) { |
| 410 if (a.start !== b.start) | 416 if (a.start !== b.start) { |
| 411 return a.start - b.start; | 417 return a.start - b.start; |
| 418 } |
| 412 return a.guid - b.guid; | 419 return a.guid - b.guid; |
| 413 }); | 420 }); |
| 414 }, | 421 }, |
| 415 | 422 |
| 416 // Find rasterization slices that have the source_prepare_tiles_id | 423 // Find rasterization slices that have the source_prepare_tiles_id |
| 417 // same as the prepare_tiles_id of TileManager::PrepareTiles | 424 // same as the prepare_tiles_id of TileManager::PrepareTiles |
| 418 // The C++ CL that makes this connection is at: | 425 // The C++ CL that makes this connection is at: |
| 419 // https://codereview.chromium.org/1208683002/ | 426 // https://codereview.chromium.org/1208683002/ |
| 420 addRasterizationEvents: function(prepareTiles, rendererHelper, | 427 addRasterizationEvents: function(prepareTiles, rendererHelper, |
| 421 visited, flowEvents, sortedRasterizerSlices) { | 428 visited, flowEvents, sortedRasterizerSlices) { |
| 422 if (!prepareTiles.args.prepare_tiles_id) | 429 if (!prepareTiles.args.prepare_tiles_id) return; |
| 430 |
| 431 if (!rendererHelper || !rendererHelper.rasterWorkerThreads) { |
| 423 return; | 432 return; |
| 424 | 433 } |
| 425 if (!rendererHelper || !rendererHelper.rasterWorkerThreads) | |
| 426 return; | |
| 427 | 434 |
| 428 var rasterWorkerThreads = rendererHelper.rasterWorkerThreads; | 435 var rasterWorkerThreads = rendererHelper.rasterWorkerThreads; |
| 429 var prepareTileId = prepareTiles.args.prepare_tiles_id; | 436 var prepareTileId = prepareTiles.args.prepare_tiles_id; |
| 430 var pendingEventQueue = []; | 437 var pendingEventQueue = []; |
| 431 | 438 |
| 432 // Collect all the rasterizer tasks. Return the cached copy if possible. | 439 // Collect all the rasterizer tasks. Return the cached copy if possible. |
| 433 if (sortedRasterizerSlices.length === 0) | 440 if (sortedRasterizerSlices.length === 0) { |
| 434 this.sortRasterizerSlices(rasterWorkerThreads, sortedRasterizerSlices); | 441 this.sortRasterizerSlices(rasterWorkerThreads, sortedRasterizerSlices); |
| 442 } |
| 435 | 443 |
| 436 // TODO(yuhao): Once TaskSetFinishedTaskImpl also get the prepareTileId | 444 // TODO(yuhao): Once TaskSetFinishedTaskImpl also get the prepareTileId |
| 437 // we can simply track by checking id rather than counting. | 445 // we can simply track by checking id rather than counting. |
| 438 var numFinishedTasks = 0; | 446 var numFinishedTasks = 0; |
| 439 var RASTER_TASK_TITLE = 'RasterizerTaskImpl::RunOnWorkerThread'; | 447 var RASTER_TASK_TITLE = 'RasterizerTaskImpl::RunOnWorkerThread'; |
| 440 var IMAGEDECODE_TASK_TITLE = 'ImageDecodeTaskImpl::RunOnWorkerThread'; | 448 var IMAGEDECODE_TASK_TITLE = 'ImageDecodeTaskImpl::RunOnWorkerThread'; |
| 441 var FINISHED_TASK_TITLE = 'TaskSetFinishedTaskImpl::RunOnWorkerThread'; | 449 var FINISHED_TASK_TITLE = 'TaskSetFinishedTaskImpl::RunOnWorkerThread'; |
| 442 | 450 |
| 443 for (var i = 0; i < sortedRasterizerSlices.length; i++) { | 451 for (var i = 0; i < sortedRasterizerSlices.length; i++) { |
| 444 var task = sortedRasterizerSlices[i]; | 452 var task = sortedRasterizerSlices[i]; |
| 445 | 453 |
| 446 if (task.title === RASTER_TASK_TITLE || | 454 if (task.title === RASTER_TASK_TITLE || |
| 447 task.title === IMAGEDECODE_TASK_TITLE) { | 455 task.title === IMAGEDECODE_TASK_TITLE) { |
| 448 if (task.args.source_prepare_tiles_id === prepareTileId) | 456 if (task.args.source_prepare_tiles_id === prepareTileId) { |
| 449 this.addEntireSliceHierarchy(task.mostTopLevelSlice); | 457 this.addEntireSliceHierarchy(task.mostTopLevelSlice); |
| 458 } |
| 450 } else if (task.title === FINISHED_TASK_TITLE) { | 459 } else if (task.title === FINISHED_TASK_TITLE) { |
| 451 if (task.start > prepareTiles.start) { | 460 if (task.start > prepareTiles.start) { |
| 452 pendingEventQueue.push(task.mostTopLevelSlice); | 461 pendingEventQueue.push(task.mostTopLevelSlice); |
| 453 if (++numFinishedTasks === 3) | 462 if (++numFinishedTasks === 3) break; |
| 454 break; | |
| 455 } | 463 } |
| 456 } | 464 } |
| 457 } | 465 } |
| 458 | 466 |
| 459 // Trace PostTask from rasterizer tasks. | 467 // Trace PostTask from rasterizer tasks. |
| 460 while (pendingEventQueue.length !== 0) { | 468 while (pendingEventQueue.length !== 0) { |
| 461 var event = pendingEventQueue.pop(); | 469 var event = pendingEventQueue.pop(); |
| 462 | 470 |
| 463 this.addEntireSliceHierarchy(event); | 471 this.addEntireSliceHierarchy(event); |
| 464 this.followSubsequentSlices(event, pendingEventQueue, visited, | 472 this.followSubsequentSlices(event, pendingEventQueue, visited, |
| (...skipping 26 matching lines...) Expand all Loading... |
| 491 this.followCurrentSlice(event, pendingEventQueue, visitedEvents); | 499 this.followCurrentSlice(event, pendingEventQueue, visitedEvents); |
| 492 | 500 |
| 493 this.followSubsequentSlices(event, pendingEventQueue, visitedEvents, | 501 this.followSubsequentSlices(event, pendingEventQueue, visitedEvents, |
| 494 flowEvents); | 502 flowEvents); |
| 495 | 503 |
| 496 // The rasterization work (CompositorTileWorker thread) and the | 504 // The rasterization work (CompositorTileWorker thread) and the |
| 497 // Compositor tile manager are connect by the prepare_tiles_id | 505 // Compositor tile manager are connect by the prepare_tiles_id |
| 498 // instead of flow events. | 506 // instead of flow events. |
| 499 var COMPOSITOR_PREPARE_TILES = 'TileManager::PrepareTiles'; | 507 var COMPOSITOR_PREPARE_TILES = 'TileManager::PrepareTiles'; |
| 500 prepareTiles = event.findDescendentSlice(COMPOSITOR_PREPARE_TILES); | 508 prepareTiles = event.findDescendentSlice(COMPOSITOR_PREPARE_TILES); |
| 501 if (prepareTiles) | 509 if (prepareTiles) { |
| 502 this.addRasterizationEvents(prepareTiles, rendererHelper, | 510 this.addRasterizationEvents(prepareTiles, rendererHelper, |
| 503 visitedEvents, flowEvents, sortedRasterizerSlices); | 511 visitedEvents, flowEvents, sortedRasterizerSlices); |
| 512 } |
| 504 | 513 |
| 505 // OnBeginImplFrameDeadline could be triggered by other inputs. | 514 // OnBeginImplFrameDeadline could be triggered by other inputs. |
| 506 // For now, we backtrace from it. | 515 // For now, we backtrace from it. |
| 507 // TODO(yuhao): There are more such slices that we need to backtrace | 516 // TODO(yuhao): There are more such slices that we need to backtrace |
| 508 var COMPOSITOR_ON_BIFD = 'Scheduler::OnBeginImplFrameDeadline'; | 517 var COMPOSITOR_ON_BIFD = 'Scheduler::OnBeginImplFrameDeadline'; |
| 509 beginImplFrame = event.findDescendentSlice(COMPOSITOR_ON_BIFD); | 518 beginImplFrame = event.findDescendentSlice(COMPOSITOR_ON_BIFD); |
| 510 if (beginImplFrame) | 519 if (beginImplFrame) { |
| 511 this.backtraceFromDraw(beginImplFrame, visitedEvents); | 520 this.backtraceFromDraw(beginImplFrame, visitedEvents); |
| 521 } |
| 512 } | 522 } |
| 513 | 523 |
| 514 // A separate pass on GestureScrollUpdate. | 524 // A separate pass on GestureScrollUpdate. |
| 515 // Scroll update doesn't go through the main thread, but the compositor | 525 // Scroll update doesn't go through the main thread, but the compositor |
| 516 // may go back to the main thread if there is an onscroll event handler. | 526 // may go back to the main thread if there is an onscroll event handler. |
| 517 // This is captured by a different flow event, which does not have the | 527 // This is captured by a different flow event, which does not have the |
| 518 // same ID as the Input Latency Event, but it is technically causally | 528 // same ID as the Input Latency Event, but it is technically causally |
| 519 // related to the GestureScrollUpdate input. Add them manually for now. | 529 // related to the GestureScrollUpdate input. Add them manually for now. |
| 520 var INPUT_GSU = 'InputLatency::GestureScrollUpdate'; | 530 var INPUT_GSU = 'InputLatency::GestureScrollUpdate'; |
| 521 if (this.title === INPUT_GSU) | 531 if (this.title === INPUT_GSU) { |
| 522 this.addScrollUpdateEvents(rendererHelper); | 532 this.addScrollUpdateEvents(rendererHelper); |
| 533 } |
| 523 }, | 534 }, |
| 524 | 535 |
| 525 get associatedEvents() { | 536 get associatedEvents() { |
| 526 if (this.associatedEvents_.length !== 0) | 537 if (this.associatedEvents_.length !== 0) { |
| 527 return this.associatedEvents_; | 538 return this.associatedEvents_; |
| 539 } |
| 528 | 540 |
| 529 var modelIndices = this.startThread.parent.model.modelIndices; | 541 var modelIndices = this.startThread.parent.model.modelIndices; |
| 530 var flowEvents = modelIndices.getFlowEventsWithId(this.id); | 542 var flowEvents = modelIndices.getFlowEventsWithId(this.id); |
| 531 | 543 |
| 532 if (flowEvents.length === 0) | 544 if (flowEvents.length === 0) { |
| 533 return this.associatedEvents_; | 545 return this.associatedEvents_; |
| 546 } |
| 534 | 547 |
| 535 // Step 1: Get events that are directly connected by the LatencyInfo | 548 // Step 1: Get events that are directly connected by the LatencyInfo |
| 536 // flow events. This gives us a small set of events that are guaranteed | 549 // flow events. This gives us a small set of events that are guaranteed |
| 537 // to be associated with the input, but are almost certain incomplete. | 550 // to be associated with the input, but are almost certain incomplete. |
| 538 // We call this set "source" event set. | 551 // We call this set "source" event set. |
| 539 // This step returns the "source" event set (sourceSlices), which is then | 552 // This step returns the "source" event set (sourceSlices), which is then |
| 540 // used in the second step. | 553 // used in the second step. |
| 541 var sourceSlices = this.addDirectlyAssociatedEvents(flowEvents); | 554 var sourceSlices = this.addDirectlyAssociatedEvents(flowEvents); |
| 542 | 555 |
| 543 // Step 2: Start from the previously constructed "source" event set, we | 556 // Step 2: Start from the previously constructed "source" event set, we |
| 544 // follow the toplevel (i.e., PostTask) and IPC flow events. Any slices | 557 // follow the toplevel (i.e., PostTask) and IPC flow events. Any slices |
| 545 // that are reachable from the "source" event set via PostTasks or IPCs | 558 // that are reachable from the "source" event set via PostTasks or IPCs |
| 546 // are conservatively considered associated with the input event. | 559 // are conservatively considered associated with the input event. |
| 547 // We then deal with specific cases where flow events either over include | 560 // We then deal with specific cases where flow events either over include |
| 548 // or miss capturing slices. | 561 // or miss capturing slices. |
| 549 var rendererHelper = this.getRendererHelper(sourceSlices); | 562 var rendererHelper = this.getRendererHelper(sourceSlices); |
| 550 this.addOtherCausallyRelatedEvents(rendererHelper, sourceSlices, | 563 this.addOtherCausallyRelatedEvents(rendererHelper, sourceSlices, |
| 551 flowEvents); | 564 flowEvents); |
| 552 | 565 |
| 553 return this.associatedEvents_; | 566 return this.associatedEvents_; |
| 554 }, | 567 }, |
| 555 | 568 |
| 556 get inputLatency() { | 569 get inputLatency() { |
| 557 if (!('data' in this.args)) | 570 if (!('data' in this.args)) return undefined; |
| 558 return undefined; | |
| 559 | 571 |
| 560 var data = this.args.data; | 572 var data = this.args.data; |
| 561 if (!(END_COMP_NAME in data)) | 573 if (!(END_COMP_NAME in data)) return undefined; |
| 562 return undefined; | |
| 563 | 574 |
| 564 var latency = 0; | 575 var latency = 0; |
| 565 var endTime = data[END_COMP_NAME].time; | 576 var endTime = data[END_COMP_NAME].time; |
| 566 if (ORIGINAL_COMP_NAME in data) { | 577 if (ORIGINAL_COMP_NAME in data) { |
| 567 latency = endTime - data[ORIGINAL_COMP_NAME].time; | 578 latency = endTime - data[ORIGINAL_COMP_NAME].time; |
| 568 } else if (UI_COMP_NAME in data) { | 579 } else if (UI_COMP_NAME in data) { |
| 569 latency = endTime - data[UI_COMP_NAME].time; | 580 latency = endTime - data[UI_COMP_NAME].time; |
| 570 } else if (BEGIN_COMP_NAME in data) { | 581 } else if (BEGIN_COMP_NAME in data) { |
| 571 latency = endTime - data[BEGIN_COMP_NAME].time; | 582 latency = endTime - data[BEGIN_COMP_NAME].time; |
| 572 } else { | 583 } else { |
| (...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 622 typeNames: allTypeNames, | 633 typeNames: allTypeNames, |
| 623 categoryParts: ['latencyInfo'] | 634 categoryParts: ['latencyInfo'] |
| 624 }); | 635 }); |
| 625 | 636 |
| 626 return { | 637 return { |
| 627 InputLatencyAsyncSlice, | 638 InputLatencyAsyncSlice, |
| 628 INPUT_EVENT_TYPE_NAMES, | 639 INPUT_EVENT_TYPE_NAMES, |
| 629 }; | 640 }; |
| 630 }); | 641 }); |
| 631 </script> | 642 </script> |
| OLD | NEW |