 Chromium Code Reviews
 Chromium Code Reviews Issue 1963583005:
  Rewrite firstPaintMetric to support extracting multiple navigations from a single trace  (Closed) 
  Base URL: https://chromium.googlesource.com/external/github.com/catapult-project/catapult.git@master
    
  
    Issue 1963583005:
  Rewrite firstPaintMetric to support extracting multiple navigations from a single trace  (Closed) 
  Base URL: https://chromium.googlesource.com/external/github.com/catapult-project/catapult.git@master| OLD | NEW | 
|---|---|
| 1 <!DOCTYPE html> | 1 <!DOCTYPE html> | 
| 2 <!-- | 2 <!-- | 
| 3 Copyright 2016 The Chromium Authors. All rights reserved. | 3 Copyright 2016 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/base/statistics.html"> | 8 <link rel="import" href="/tracing/base/statistics.html"> | 
| 9 <link rel="import" href="/tracing/metrics/metric_registry.html"> | 9 <link rel="import" href="/tracing/metrics/metric_registry.html"> | 
| 10 <link rel="import" href="/tracing/metrics/system_health/utils.html"> | 10 <link rel="import" href="/tracing/metrics/system_health/utils.html"> | 
| 11 <link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> | 11 <link rel="import" href="/tracing/model/helpers/chrome_model_helper.html"> | 
| 12 <link rel="import" href="/tracing/value/numeric.html"> | 12 <link rel="import" href="/tracing/value/numeric.html"> | 
| 13 <link rel="import" href="/tracing/value/value.html"> | 13 <link rel="import" href="/tracing/value/value.html"> | 
| 14 | 14 | 
| 15 <script> | 15 <script> | 
| 16 'use strict'; | 16 'use strict'; | 
| 17 | 17 | 
| 18 tr.exportTo('tr.metrics.sh', function() { | 18 tr.exportTo('tr.metrics.sh', function() { | 
| 19 var timeDurationInMs_smallerIsBetter = | 19 var timeDurationInMs_smallerIsBetter = | 
| 20 tr.v.Unit.byName.timeDurationInMs_smallerIsBetter; | 20 tr.v.Unit.byName.timeDurationInMs_smallerIsBetter; | 
| 21 | 21 | 
| 22 function findTargetRendererHelper(model) { | 22 function findTargetRendererHelper(chromeHelper) { | 
| 23 var chromeHelper = model.getOrCreateHelper( | |
| 24 tr.model.helpers.ChromeModelHelper); | |
| 25 | |
| 26 var largestPid = -1; | 23 var largestPid = -1; | 
| 27 for (var pid in chromeHelper.rendererHelpers) { | 24 for (var pid in chromeHelper.rendererHelpers) { | 
| 28 var rendererHelper = chromeHelper.rendererHelpers[pid]; | 25 var rendererHelper = chromeHelper.rendererHelpers[pid]; | 
| 29 if (rendererHelper.isChromeTracingUI) | 26 if (rendererHelper.isChromeTracingUI) | 
| 30 continue; | 27 continue; | 
| 31 if (pid > largestPid) | 28 if (pid > largestPid) | 
| 32 largestPid = pid; | 29 largestPid = pid; | 
| 33 } | 30 } | 
| 34 | 31 | 
| 35 if (largestPid === -1) | 32 if (largestPid === -1) | 
| 36 return undefined; | 33 return undefined; | 
| 37 | 34 | 
| 38 return chromeHelper.rendererHelpers[largestPid]; | 35 return chromeHelper.rendererHelpers[largestPid]; | 
| 39 } | 36 } | 
| 40 | 37 | 
| 41 function findNavigationStartEvent(rendererHelper) { | 38 function navigationStartFinder(rendererHelper) { | 
| 42 var navigationStartEvent = undefined; | 39 var navigationStartsForFrameId = {}; | 
| 43 | |
| 44 rendererHelper.mainThread.sliceGroup.iterateAllEventsInThisContainer( | 40 rendererHelper.mainThread.sliceGroup.iterateAllEventsInThisContainer( | 
| 45 () => true, function(ev) { | 41 () => true, function(ev) { | 
| 46 if (navigationStartEvent !== undefined || | 42 if (ev.category !== 'blink.user_timing' || | 
| 47 ev.category !== 'blink.user_timing') | 43 ev.title !== 'navigationStart') | 
| 48 return; | 44 return; | 
| 49 if (ev.title === 'navigationStart') | 45 | 
| 50 navigationStartEvent = ev; | 46 var frameIdRef = ev.args['frame']; | 
| 47 var list = navigationStartsForFrameId[frameIdRef]; | |
| 48 if (list === undefined) { | |
| 49 navigationStartsForFrameId[frameIdRef] = list = []; | |
| 50 } | |
| 51 list.unshift(ev); | |
| 51 }, | 52 }, | 
| 52 this); | 53 this); | 
| 53 | 54 | 
| 54 return navigationStartEvent; | 55 return function findNavigationStartEventForFrameBeforeTimestamp(frameIdRef, | 
| 56 ts) { | |
| 57 var list = navigationStartsForFrameId[frameIdRef]; | |
| 58 if (list === undefined) | |
| 59 throw new Error('No navigationStartEvent found for frame id "' + | |
| 60 frameIdRef + '"'); | |
| 61 | |
| 62 var eventBeforeTimestamp; | |
| 63 list.forEach(function(ev) { | |
| 64 if (ev.start > ts) | |
| 65 return; | |
| 66 | |
| 67 if (eventBeforeTimestamp === undefined) | |
| 68 eventBeforeTimestamp = ev; | |
| 69 }, this); | |
| 70 if (eventBeforeTimestamp === undefined) | |
| 71 throw new Error('Failed to find navigationStartEvent.'); | |
| 72 return eventBeforeTimestamp; | |
| 73 } | |
| 55 } | 74 } | 
| 56 | 75 | 
| 57 function findFirstPaintEvent(rendererHelper, title, frame) { | 76 function findUrlOfFrameAt(rendererHelper, frameIdRef, ts) { | 
| 58 var firstPaintEvent = undefined; | 77 var url; | 
| 78 | |
| 79 var objects = rendererHelper.process.objects; | |
| 80 var frameLoaderInstances = objects.instancesByTypeName_['FrameLoader']; | |
| 81 frameLoaderInstances.forEach(function(instance) { | |
| 82 var snapshot = instance.getSnapshotAt(ts); | |
| 83 if (frameIdRef !== snapshot.args['frame']['id_ref']) | |
| 84 return; | |
| 85 | |
| 86 url = snapshot.args['documentLoaderURL']; | |
| 87 }, this); | |
| 88 | |
| 89 return url; | |
| 90 } | |
| 91 | |
| 92 function findFirstPaintEvents(rendererHelper, title) { | |
| 93 var firstPaintEvents = []; | |
| 59 | 94 | 
| 60 rendererHelper.process.iterateAllEvents( | 95 rendererHelper.process.iterateAllEvents( | 
| 61 function(ev) { | 96 function(ev) { | 
| 62 if (firstPaintEvent !== undefined || | 97 if (ev.category !== 'blink.user_timing' || | 
| 63 ev.category !== 'blink.user_timing' || | 98 ev.title !== title) | 
| 64 ev.title !== title || | |
| 65 ev.args === undefined || ev.args['frame'] !== frame) | |
| 66 return; | 99 return; | 
| 67 | 100 | 
| 68 firstPaintEvent = ev; | 101 firstPaintEvents.push(ev); | 
| 69 }, this); | 102 }, this); | 
| 70 | 103 | 
| 71 return firstPaintEvent; | 104 return firstPaintEvents; | 
| 105 } | |
| 106 | |
| 107 function prepareTelemetryInternalEventPredicate(rendererHelper) { | |
| 108 var ignoreRegionSlices = []; | |
| 109 rendererHelper.mainThread.asyncSliceGroup.iterateAllEventsInThisContainer( | |
| 110 () => true, function(slice) { | |
| 111 if (slice.title.match(/^telemetry\.internal/)) | |
| 112 ignoreRegionSlices.push(slice); | |
| 113 }, this); | |
| 114 | |
| 115 return function isTelemetryInternalEvent(slice) { | |
| 116 for (var i = 0; i < ignoreRegionSlices.length; ++ i) { | |
| 117 if (ignoreRegionSlices[i].bounds(slice)) | |
| 118 return true; | |
| 119 } | |
| 120 return false; | |
| 121 } | |
| 72 } | 122 } | 
| 73 | 123 | 
| 74 function firstPaintMetric(valueList, model) { | 124 function firstPaintMetric(valueList, model) { | 
| 75 var rendererHelper = findTargetRendererHelper(model); | 125 var chromeHelper = model.getOrCreateHelper( | 
| 76 var navigationStartEvent = findNavigationStartEvent(rendererHelper); | 126 tr.model.helpers.ChromeModelHelper); | 
| 127 var rendererHelper = findTargetRendererHelper(chromeHelper); | |
| 128 var isTelemetryInternalEvent = | |
| 129 prepareTelemetryInternalEventPredicate(rendererHelper); | |
| 130 var findNavigationStartEventForFrameBeforeTimestamp = | |
| 131 navigationStartFinder(rendererHelper); | |
| 77 | 132 | 
| 78 if (navigationStartEvent === undefined) | 133 var firstPaintEvents = findFirstPaintEvents(rendererHelper, | 
| 79 throw new Error('Failed to find navigationStartEvent.'); | 134 'firstContentfulPaint'); | 
| 135 firstPaintEvents = firstPaintEvents.filter( | |
| 136 (ev) => !isTelemetryInternalEvent(ev)); | |
| 137 firstPaintEvents.forEach(function(ev) { | |
| 138 var frameIdRef = ev.args['frame']; | |
| 139 var url = findUrlOfFrameAt(rendererHelper, frameIdRef, ev.start); | |
| 140 var navigationStartEvent = | |
| 141 findNavigationStartEventForFrameBeforeTimestamp(frameIdRef, ev.start); | |
| 80 | 142 | 
| 81 var frame = navigationStartEvent.args['frame']; | 143 var timeToFirstContentfulPaint = | 
| 82 var firstContentfulPaintEvent = findFirstPaintEvent(rendererHelper, | 144 ev.start - navigationStartEvent.start; | 
| 83 'firstContentfulPaint', frame); | |
| 84 if (firstContentfulPaintEvent === undefined) | |
| 85 throw new Error( | |
| 86 'Failed to find firstContentfulPaintEvent for frame ' + frame); | |
| 87 | 145 | 
| 88 var grouping_keys = {}; | 146 var grouping_keys = {url: url}; | 
| 89 | 147 valueList.addValue(new tr.v.NumericValue( | 
| 
benjhayden
2016/05/10 19:54:46
Instead of several scalars, do you want to use a N
 
kouhei (in TOK)
2016/05/11 06:39:33
I think we want separate individual values, as his
 
nednguyen
2016/05/11 21:36:38
Why do you think the histogram doesn't make sense?
 
kouhei (in TOK)
2016/05/12 05:41:25
Added Numeric histogram, but not sure how we can u
 
nednguyen
2016/05/12 15:35:30
For future use cases of Pcv2, I can imagine we hav
 | |
| 90 var timeToFirstContentfulPaint = | 148 model.canonicalUrlThatCreatedThisTrace, 'firstContentfulPaint', | 
| 
benjhayden
2016/05/10 20:25:43
This is spelled "model.canonicalUrl" now.
 
kouhei (in TOK)
2016/05/11 06:39:33
Done.
 | |
| 91 firstContentfulPaintEvent.start - navigationStartEvent.start; | 149 new tr.v.ScalarNumeric(timeDurationInMs_smallerIsBetter, | 
| 92 valueList.addValue(new tr.v.NumericValue( | 150 timeToFirstContentfulPaint), | 
| 93 model.canonicalUrlThatCreatedThisTrace, 'firstContentfulPaint', | 151 { description: 'time to first contentful paint' }, | 
| 94 new tr.v.ScalarNumeric(timeDurationInMs_smallerIsBetter, | 152 grouping_keys)); | 
| 95 timeToFirstContentfulPaint), | 153 }, this); | 
| 96 { description: 'time to first contentful paint' }, | |
| 97 grouping_keys)); | |
| 98 } | 154 } | 
| 99 | 155 | 
| 100 firstPaintMetric.prototype = { | 156 firstPaintMetric.prototype = { | 
| 101 __proto__: Function.prototype | 157 __proto__: Function.prototype | 
| 102 }; | 158 }; | 
| 103 | 159 | 
| 104 tr.metrics.MetricRegistry.register(firstPaintMetric); | 160 tr.metrics.MetricRegistry.register(firstPaintMetric); | 
| 105 | 161 | 
| 106 return { | 162 return { | 
| 107 firstPaintMetric: firstPaintMetric | 163 firstPaintMetric: firstPaintMetric | 
| 108 }; | 164 }; | 
| 109 }); | 165 }); | 
| 110 </script> | 166 </script> | 
| OLD | NEW |