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/range.html"> | 8 <link rel="import" href="/tracing/base/range.html"> |
9 <link rel="import" href="/tracing/base/unit.html"> | 9 <link rel="import" href="/tracing/base/unit.html"> |
10 <link rel="import" href="/tracing/extras/v8/runtime_stats_entry.html"> | 10 <link rel="import" href="/tracing/extras/v8/runtime_stats_entry.html"> |
11 <link rel="import" href="/tracing/metrics/metric_registry.html"> | 11 <link rel="import" href="/tracing/metrics/metric_registry.html"> |
12 <link rel="import" href="/tracing/metrics/system_health/loading_metric.html"> | 12 <link rel="import" href="/tracing/metrics/system_health/loading_metric.html"> |
13 <link rel="import" href="/tracing/value/histogram.html"> | 13 <link rel="import" href="/tracing/value/histogram.html"> |
14 | 14 |
15 <script> | 15 <script> |
16 'use strict'; | 16 'use strict'; |
17 | 17 |
18 tr.exportTo('tr.metrics.v8', function() { | 18 tr.exportTo('tr.metrics.v8', function() { |
19 var COUNT_CUSTOM_BOUNDARIES = tr.v.HistogramBinBoundaries | 19 const COUNT_CUSTOM_BOUNDARIES = tr.v.HistogramBinBoundaries |
20 .createExponential(1, 1000000, 50); | 20 .createExponential(1, 1000000, 50); |
21 var DURATION_CUSTOM_BOUNDARIES = tr.v.HistogramBinBoundaries | 21 const DURATION_CUSTOM_BOUNDARIES = tr.v.HistogramBinBoundaries |
22 .createExponential(0.1, 10000, 50); | 22 .createExponential(0.1, 10000, 50); |
23 | 23 |
24 function computeDomContentLoadedTime_(model) { | 24 function computeDomContentLoadedTime_(model) { |
25 var chromeHelper = model.getOrCreateHelper( | 25 const chromeHelper = model.getOrCreateHelper( |
26 tr.model.helpers.ChromeModelHelper); | 26 tr.model.helpers.ChromeModelHelper); |
27 var domContentLoadedTime = 0; | 27 var domContentLoadedTime = 0; |
28 | 28 |
29 for (var rendererHelper of Object.values(chromeHelper.rendererHelpers)) { | 29 for (let rendererHelper of Object.values(chromeHelper.rendererHelpers)) { |
30 for (var ev of rendererHelper.mainThread.sliceGroup.childEvents()) { | 30 for (let ev of rendererHelper.mainThread.sliceGroup.childEvents()) { |
31 if (ev.title === 'domContentLoadedEventEnd' && | 31 if (ev.title === 'domContentLoadedEventEnd' && |
32 ev.start > domContentLoadedTime) { | 32 ev.start > domContentLoadedTime) { |
33 domContentLoadedTime = ev.start; | 33 domContentLoadedTime = ev.start; |
34 } | 34 } |
35 } | 35 } |
36 } | 36 } |
37 return domContentLoadedTime; | 37 return domContentLoadedTime; |
38 } | 38 } |
39 | 39 |
40 function computeInteractiveTime_(model) { | 40 function computeInteractiveTime_(model) { |
41 var chromeHelper = model.getOrCreateHelper( | 41 const chromeHelper = model.getOrCreateHelper( |
42 tr.model.helpers.ChromeModelHelper); | 42 tr.model.helpers.ChromeModelHelper); |
43 var interactiveTime = 0; | 43 let interactiveTime = 0; |
44 for (var rendererHelper of Object.values(chromeHelper.rendererHelpers)) { | 44 for (let rendererHelper of Object.values(chromeHelper.rendererHelpers)) { |
45 var samples = tr.metrics.sh.collectLoadingMetricsForRenderer( | 45 let samples = tr.metrics.sh.collectLoadingMetricsForRenderer( |
46 rendererHelper).firstInteractiveSamples; | 46 rendererHelper).firstInteractiveSamples; |
47 // TODO(fmeawad): Support multiple navigations. | 47 // TODO(fmeawad): Support multiple navigations. |
48 if (samples.length === 0) continue; | 48 if (samples.length === 0) continue; |
49 if (interactiveTime !== 0) throw new Error('Too many navigations'); | 49 if (interactiveTime !== 0) throw new Error('Too many navigations'); |
50 interactiveTime = tr.b.getOnlyElement(samples).diagnostics[ | 50 interactiveTime = tr.b.getOnlyElement(samples).diagnostics[ |
51 'Navigation infos'].value.interactive; | 51 'Navigation infos'].value.interactive; |
52 } | 52 } |
53 return interactiveTime; | 53 return interactiveTime; |
54 } | 54 } |
55 | 55 |
56 function createDurationHistogram_(name) { | 56 function createDurationHistogram_(name) { |
57 var histogram = new tr.v.Histogram(name + ':duration', | 57 let histogram = new tr.v.Histogram(name + ':duration', |
58 tr.b.Unit.byName.timeDurationInMs_smallerIsBetter, | 58 tr.b.Unit.byName.timeDurationInMs_smallerIsBetter, |
59 DURATION_CUSTOM_BOUNDARIES); | 59 DURATION_CUSTOM_BOUNDARIES); |
60 histogram.customizeSummaryOptions({ | 60 histogram.customizeSummaryOptions({ |
61 std: false, count: false, sum: false, min: false, max: false | 61 std: false, count: false, sum: false, min: false, max: false |
62 }); | 62 }); |
63 return histogram; | 63 return histogram; |
64 } | 64 } |
65 | 65 |
66 function createCountHistogram_(name) { | 66 function createCountHistogram_(name) { |
67 var histogram = new tr.v.Histogram(name + ':count', | 67 let histogram = new tr.v.Histogram(name + ':count', |
68 tr.b.Unit.byName.count_smallerIsBetter, | 68 tr.b.Unit.byName.count_smallerIsBetter, |
69 COUNT_CUSTOM_BOUNDARIES); | 69 COUNT_CUSTOM_BOUNDARIES); |
70 histogram.customizeSummaryOptions({ | 70 histogram.customizeSummaryOptions({ |
71 std: false, count: false, sum: false, min: false, max: false | 71 std: false, count: false, sum: false, min: false, max: false |
72 }); | 72 }); |
73 return histogram; | 73 return histogram; |
74 } | 74 } |
75 | 75 |
76 function convertMicroToMilli_(time) { | 76 function convertMicroToMilli_(time) { |
77 return tr.b.convertUnit(time, | 77 return tr.b.convertUnit(time, |
78 tr.b.UnitPrefixScale.METRIC.MICRO, tr.b.UnitPrefixScale.METRIC.MILLI); | 78 tr.b.UnitPrefixScale.METRIC.MICRO, tr.b.UnitPrefixScale.METRIC.MILLI); |
79 } | 79 } |
80 | 80 |
81 // TODO(crbug.com/688342): Remove this function when runtimeStatsMetric is | 81 // TODO(crbug.com/688342): Remove this function when runtimeStatsMetric is |
82 // removed. | 82 // removed. |
83 function computeRuntimeStats(histograms, slices) { | 83 function computeRuntimeStats(histograms, slices) { |
84 var runtimeGroupCollection = new tr.e.v8.RuntimeStatsGroupCollection(); | 84 let runtimeGroupCollection = new tr.e.v8.RuntimeStatsGroupCollection(); |
85 runtimeGroupCollection.addSlices(slices); | 85 runtimeGroupCollection.addSlices(slices); |
86 | 86 |
87 for (var runtimeGroup of runtimeGroupCollection.runtimeGroups) { | 87 for (let runtimeGroup of runtimeGroupCollection.runtimeGroups) { |
88 var durationSamples = new tr.v.d.RelatedHistogramBreakdown(); | 88 const durationSamples = new tr.v.d.RelatedHistogramBreakdown(); |
89 var countSamples = new tr.v.d.RelatedHistogramBreakdown(); | 89 const countSamples = new tr.v.d.RelatedHistogramBreakdown(); |
90 for (var entry of runtimeGroup.values) { | 90 for (let entry of runtimeGroup.values) { |
91 var durationSampleHistogram = createDurationHistogram_(entry.name); | 91 const durationSampleHistogram = createDurationHistogram_(entry.name); |
92 durationSampleHistogram.addSample(convertMicroToMilli_(entry.time)); | 92 durationSampleHistogram.addSample(convertMicroToMilli_(entry.time)); |
93 durationSamples.set(entry.name + ':duration', durationSampleHistogram); | 93 durationSamples.set(entry.name + ':duration', durationSampleHistogram); |
94 histograms.addHistogram(durationSampleHistogram); | 94 histograms.addHistogram(durationSampleHistogram); |
95 | 95 |
96 var countSampleHistogram = createCountHistogram_(entry.name); | 96 const countSampleHistogram = createCountHistogram_(entry.name); |
97 countSampleHistogram.addSample(entry.count); | 97 countSampleHistogram.addSample(entry.count); |
98 countSamples.set(entry.name + ':count', countSampleHistogram); | 98 countSamples.set(entry.name + ':count', countSampleHistogram); |
99 histograms.addHistogram(countSampleHistogram); | 99 histograms.addHistogram(countSampleHistogram); |
100 } | 100 } |
101 | 101 |
102 var durationHistogram = createDurationHistogram_(runtimeGroup.name); | 102 const durationHistogram = createDurationHistogram_(runtimeGroup.name); |
103 durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time), { | 103 durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time), { |
104 samples: durationSamples | 104 samples: durationSamples |
105 }); | 105 }); |
106 var countHistogram = createCountHistogram_(runtimeGroup.name); | 106 const countHistogram = createCountHistogram_(runtimeGroup.name); |
107 countHistogram.addSample(runtimeGroup.count, { | 107 countHistogram.addSample(runtimeGroup.count, { |
108 samples: countSamples | 108 samples: countSamples |
109 }); | 109 }); |
110 | 110 |
111 histograms.addHistogram(durationHistogram); | 111 histograms.addHistogram(durationHistogram); |
112 histograms.addHistogram(countHistogram); | 112 histograms.addHistogram(countHistogram); |
113 } | 113 } |
114 } | 114 } |
115 | 115 |
116 // TODO(crbug.com/688342): Remove this metric and use runtimeStatsTotalMetric | 116 // TODO(crbug.com/688342): Remove this metric and use runtimeStatsTotalMetric |
117 // instead when the runtimeStatsTotalMetric is stable. | 117 // instead when the runtimeStatsTotalMetric is stable. |
118 function runtimeStatsMetric(histograms, model) { | 118 function runtimeStatsMetric(histograms, model) { |
119 var interactiveTime = computeInteractiveTime_(model); | 119 let interactiveTime = computeInteractiveTime_(model); |
120 var domContentLoadedTime = computeDomContentLoadedTime_(model); | 120 let domContentLoadedTime = computeDomContentLoadedTime_(model); |
121 var endTime = Math.max(interactiveTime, domContentLoadedTime); | 121 let endTime = Math.max(interactiveTime, domContentLoadedTime); |
122 var slices = [...model.getDescendantEvents()].filter(event => | 122 let slices = [...model.getDescendantEvents()].filter(event => |
123 event instanceof tr.e.v8.V8ThreadSlice && event.start <= endTime); | 123 event instanceof tr.e.v8.V8ThreadSlice && event.start <= endTime); |
124 computeRuntimeStats(histograms, slices); | 124 computeRuntimeStats(histograms, slices); |
125 } | 125 } |
126 | 126 |
127 function computeRuntimeStatsBucketOnUE(histograms, slices, | 127 function computeRuntimeStatsBucketOnUE(histograms, slices, |
128 v8SlicesBucketOnUEMap) { | 128 v8SlicesBucketOnUEMap) { |
129 let durationRelatedHistsByGroupName = new Map(); | 129 const durationRelatedHistsByGroupName = new Map(); |
130 let countRelatedHistsByGroupName = new Map(); | 130 const countRelatedHistsByGroupName = new Map(); |
131 | 131 |
132 // Compute runtimeStats in each of the UE buckets. Also record the | 132 // Compute runtimeStats in each of the UE buckets. Also record the |
133 // histograms in RelatedHistogramMap. These histograms are added to the | 133 // histograms in RelatedHistogramMap. These histograms are added to the |
134 // corresponding histograms in the total bucket as a diagnostic. This keeps | 134 // corresponding histograms in the total bucket as a diagnostic. This keeps |
135 // the data grouped. | 135 // the data grouped. |
136 for (var [name, slicesUE] of v8SlicesBucketOnUEMap) { | 136 for (let [name, slicesUE] of v8SlicesBucketOnUEMap) { |
137 var runtimeGroupCollection = new tr.e.v8.RuntimeStatsGroupCollection(); | 137 let runtimeGroupCollection = new tr.e.v8.RuntimeStatsGroupCollection(); |
138 runtimeGroupCollection.addSlices(slicesUE); | 138 runtimeGroupCollection.addSlices(slicesUE); |
139 | 139 |
140 for (var runtimeGroup of runtimeGroupCollection.runtimeGroups) { | 140 for (let runtimeGroup of runtimeGroupCollection.runtimeGroups) { |
141 var histogramName = name + '_' + runtimeGroup.name; | 141 const histogramName = name + '_' + runtimeGroup.name; |
142 var durationHistogram = createDurationHistogram_(histogramName); | 142 let durationHistogram = createDurationHistogram_(histogramName); |
143 durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time)); | 143 durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time)); |
144 histograms.addHistogram(durationHistogram); | 144 histograms.addHistogram(durationHistogram); |
145 | 145 |
146 // Record this histogram in RelatedHistogramMap. | 146 // Record this histogram in RelatedHistogramMap. |
147 if (durationRelatedHistsByGroupName.get(runtimeGroup.name) === | 147 if (durationRelatedHistsByGroupName.get(runtimeGroup.name) === |
148 undefined) { | 148 undefined) { |
149 var durationHistogramMap = new tr.v.d.RelatedHistogramMap(); | 149 let durationHistogramMap = new tr.v.d.RelatedHistogramMap(); |
150 durationHistogramMap.set(name, durationHistogram); | 150 durationHistogramMap.set(name, durationHistogram); |
151 durationRelatedHistsByGroupName.set(runtimeGroup.name, | 151 durationRelatedHistsByGroupName.set(runtimeGroup.name, |
152 durationHistogramMap); | 152 durationHistogramMap); |
153 } else { | 153 } else { |
154 durationRelatedHistsByGroupName.get(runtimeGroup.name).set(name, | 154 durationRelatedHistsByGroupName.get(runtimeGroup.name).set(name, |
155 durationHistogram); | 155 durationHistogram); |
156 } | 156 } |
157 | 157 |
158 var countHistogram = createCountHistogram_(histogramName); | 158 let countHistogram = createCountHistogram_(histogramName); |
159 countHistogram.addSample(runtimeGroup.count); | 159 countHistogram.addSample(runtimeGroup.count); |
160 histograms.addHistogram(countHistogram); | 160 histograms.addHistogram(countHistogram); |
161 | 161 |
162 // Record this histogram in RelatedHistogramMap. | 162 // Record this histogram in RelatedHistogramMap. |
163 if (countRelatedHistsByGroupName.get(runtimeGroup.name) === undefined) { | 163 if (countRelatedHistsByGroupName.get(runtimeGroup.name) === undefined) { |
164 var countHistogramMap = new tr.v.d.RelatedHistogramMap(); | 164 let countHistogramMap = new tr.v.d.RelatedHistogramMap(); |
165 countHistogramMap.set(name, countHistogram); | 165 countHistogramMap.set(name, countHistogram); |
166 countRelatedHistsByGroupName.set(runtimeGroup.name, | 166 countRelatedHistsByGroupName.set(runtimeGroup.name, |
167 countHistogramMap); | 167 countHistogramMap); |
168 } else { | 168 } else { |
169 countRelatedHistsByGroupName.get(runtimeGroup.name).set(name, | 169 countRelatedHistsByGroupName.get(runtimeGroup.name).set(name, |
170 countHistogram); | 170 countHistogram); |
171 } | 171 } |
172 } | 172 } |
173 } | 173 } |
174 | 174 |
175 // Add the runtimeStats for all the samples. Please note, the values in | 175 // Add the runtimeStats for all the samples. Please note, the values in |
176 // the UE buckets may not add upto the values computed here. Since UEs | 176 // the UE buckets may not add upto the values computed here. Since UEs |
177 // can overlap, we count some of the samples in multiple UE buckets. | 177 // can overlap, we count some of the samples in multiple UE buckets. |
178 var runtimeGroupCollection = new tr.e.v8.RuntimeStatsGroupCollection(); | 178 let runtimeGroupCollection = new tr.e.v8.RuntimeStatsGroupCollection(); |
179 runtimeGroupCollection.addSlices(slices); | 179 runtimeGroupCollection.addSlices(slices); |
180 for (var runtimeGroup of runtimeGroupCollection.runtimeGroups) { | 180 for (let runtimeGroup of runtimeGroupCollection.runtimeGroups) { |
181 var histogramName = runtimeGroup.name; | 181 const histogramName = runtimeGroup.name; |
182 var durationHistogram = createDurationHistogram_(histogramName); | 182 let durationHistogram = createDurationHistogram_(histogramName); |
183 durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time)); | 183 durationHistogram.addSample(convertMicroToMilli_(runtimeGroup.time)); |
184 histograms.addHistogram(durationHistogram); | 184 histograms.addHistogram(durationHistogram); |
185 // Add UE histograms as a diagnostic, so they can be hidden in the main | 185 // Add UE histograms as a diagnostic, so they can be hidden in the main |
186 // view, and the data across UE buckets can be grouped together. | 186 // view, and the data across UE buckets can be grouped together. |
187 var durationRelatedHistogram = durationRelatedHistsByGroupName.get( | 187 let durationRelatedHistogram = durationRelatedHistsByGroupName.get( |
188 runtimeGroup.name); | 188 runtimeGroup.name); |
189 if (durationRelatedHistogram !== undefined) { | 189 if (durationRelatedHistogram !== undefined) { |
190 durationHistogram.diagnostics.set('RAIL stages', | 190 durationHistogram.diagnostics.set('RAIL stages', |
191 durationRelatedHistogram); | 191 durationRelatedHistogram); |
192 } | 192 } |
193 | 193 |
194 var countHistogram = createCountHistogram_(histogramName); | 194 let countHistogram = createCountHistogram_(histogramName); |
195 countHistogram.addSample(runtimeGroup.count); | 195 countHistogram.addSample(runtimeGroup.count); |
196 // Add UE histograms as a diagnostic, so they can be hidden in the main | 196 // Add UE histograms as a diagnostic, so they can be hidden in the main |
197 // view, and the data across UE buckets can be grouped together. | 197 // view, and the data across UE buckets can be grouped together. |
198 var countRelatedHistogram = countRelatedHistsByGroupName.get( | 198 let countRelatedHistogram = countRelatedHistsByGroupName.get( |
199 runtimeGroup.name); | 199 runtimeGroup.name); |
200 if (countRelatedHistogram !== undefined) { | 200 if (countRelatedHistogram !== undefined) { |
201 countHistogram.diagnostics.set('RAIL stages', countRelatedHistogram); | 201 countHistogram.diagnostics.set('RAIL stages', countRelatedHistogram); |
202 } | 202 } |
203 histograms.addHistogram(countHistogram); | 203 histograms.addHistogram(countHistogram); |
204 } | 204 } |
205 } | 205 } |
206 | 206 |
207 function runtimeStatsTotalMetric(histograms, model) { | 207 function runtimeStatsTotalMetric(histograms, model) { |
208 var v8ThreadSlices = [...model.getDescendantEvents()].filter(event => | 208 let v8ThreadSlices = [...model.getDescendantEvents()].filter(event => |
209 event instanceof tr.e.v8.V8ThreadSlice).sort((e1, e2) => | 209 event instanceof tr.e.v8.V8ThreadSlice).sort((e1, e2) => |
210 e1.start - e2.start); | 210 e1.start - e2.start); |
211 var v8SlicesBucketOnUEMap = new Map(); | 211 let v8SlicesBucketOnUEMap = new Map(); |
212 // User expectations can sometime overlap. So, certain v8 slices can be | 212 // User expectations can sometime overlap. So, certain v8 slices can be |
213 // included in more than one expectation. We count such slices in each | 213 // included in more than one expectation. We count such slices in each |
214 // of the expectations. This is done so as to minimize the noise due to | 214 // of the expectations. This is done so as to minimize the noise due to |
215 // the differences in the extent of overlap between the runs. | 215 // the differences in the extent of overlap between the runs. |
216 for (var expectation of model.userModel.expectations) { | 216 for (let expectation of model.userModel.expectations) { |
217 var slices = expectation.range.filterArray(v8ThreadSlices, | 217 let slices = expectation.range.filterArray(v8ThreadSlices, |
218 event => event.start); | 218 event => event.start); |
219 if (slices.length === 0) continue; | 219 if (slices.length === 0) continue; |
220 // filterArray filters the array that intersects the range inclusively. | 220 // filterArray filters the array that intersects the range inclusively. |
221 // Expectations are not inclusive i.e. expectations are like [0, 1), | 221 // Expectations are not inclusive i.e. expectations are like [0, 1), |
222 // [1, 2). v8ThreadSlices that start at 1 should be counted only in [1,2) | 222 // [1, 2). v8ThreadSlices that start at 1 should be counted only in [1,2) |
223 // bucket. Filter out sample at the boundary so that they are not counted | 223 // bucket. Filter out sample at the boundary so that they are not counted |
224 // twice. | 224 // twice. |
225 var lastSlice = slices[slices.length - 1]; | 225 let lastSlice = slices[slices.length - 1]; |
226 if (!expectation.range.intersectsRangeExclusive(lastSlice.range)) { | 226 if (!expectation.range.intersectsRangeExclusive(lastSlice.range)) { |
227 slices.pop(); | 227 slices.pop(); |
228 } | 228 } |
229 | 229 |
230 if (v8SlicesBucketOnUEMap.get(expectation.stageTitle) === undefined) { | 230 if (v8SlicesBucketOnUEMap.get(expectation.stageTitle) === undefined) { |
231 v8SlicesBucketOnUEMap.set(expectation.stageTitle, slices); | 231 v8SlicesBucketOnUEMap.set(expectation.stageTitle, slices); |
232 } else { | 232 } else { |
233 var totalSlices = v8SlicesBucketOnUEMap.get(expectation.stageTitle) | 233 let totalSlices = v8SlicesBucketOnUEMap.get(expectation.stageTitle) |
234 .concat(slices); | 234 .concat(slices); |
235 v8SlicesBucketOnUEMap.set(expectation.stageTitle, totalSlices); | 235 v8SlicesBucketOnUEMap.set(expectation.stageTitle, totalSlices); |
236 } | 236 } |
237 } | 237 } |
238 | 238 |
239 // Compute runtimeStats in each of the UE buckets and also compute | 239 // Compute runtimeStats in each of the UE buckets and also compute |
240 // runtimeStats on all of the samples. The values in UE buckets do not add | 240 // runtimeStats on all of the samples. The values in UE buckets do not add |
241 // up to the total of all samples, since we duplicate some of the samples in | 241 // up to the total of all samples, since we duplicate some of the samples in |
242 // multiple buckets when the UEs overlap. | 242 // multiple buckets when the UEs overlap. |
243 computeRuntimeStatsBucketOnUE(histograms, v8ThreadSlices, | 243 computeRuntimeStatsBucketOnUE(histograms, v8ThreadSlices, |
244 v8SlicesBucketOnUEMap); | 244 v8SlicesBucketOnUEMap); |
245 } | 245 } |
246 | 246 |
247 tr.metrics.MetricRegistry.register(runtimeStatsTotalMetric); | 247 tr.metrics.MetricRegistry.register(runtimeStatsTotalMetric); |
248 tr.metrics.MetricRegistry.register(runtimeStatsMetric); | 248 tr.metrics.MetricRegistry.register(runtimeStatsMetric); |
249 | 249 |
250 return { | 250 return { |
251 runtimeStatsMetric, | 251 runtimeStatsMetric, |
252 runtimeStatsTotalMetric, | 252 runtimeStatsTotalMetric, |
253 }; | 253 }; |
254 }); | 254 }); |
255 </script> | 255 </script> |
OLD | NEW |