Index: tracing/tracing/metrics/system_health/cpu_time_metric_test.html |
diff --git a/tracing/tracing/metrics/system_health/cpu_time_metric_test.html b/tracing/tracing/metrics/system_health/cpu_time_metric_test.html |
index 3405d82e1f9e13c742e1bd324bcfcf11bfd498be..f13644c63458b135b5c0b17515945ef71b5c6907 100644 |
--- a/tracing/tracing/metrics/system_health/cpu_time_metric_test.html |
+++ b/tracing/tracing/metrics/system_health/cpu_time_metric_test.html |
@@ -7,6 +7,7 @@ found in the LICENSE file. |
<link rel="import" href="/tracing/core/test_utils.html"> |
<link rel="import" href="/tracing/extras/chrome/chrome_test_utils.html"> |
+<link rel="import" href="/tracing/extras/chrome/cpu_time_test_utils.html"> |
<link rel="import" href="/tracing/metrics/system_health/cpu_time_metric.html"> |
<link rel="import" href="/tracing/value/histogram_set.html"> |
@@ -14,146 +15,643 @@ found in the LICENSE file. |
'use strict'; |
tr.b.unittest.testSuite(function() { |
- function computeCpuTime(customizeModelCallback, opt_options) { |
- const model = tr.c.TestUtils.newModel(function(model) { |
- customizeModelCallback(model); |
- }); |
+ const getThreadType = tr.metrics.sh.getThreadType; |
+ const calculateCpuTimeFragment = tr.metrics.sh.calculateCpuTimeFragment; |
+ |
+ const CHROME_PROCESS_NAMES = |
+ tr.e.chrome.chrome_processes.CHROME_PROCESS_NAMES; |
+ |
+ const buildModelFromSpec = tr.e.chrome.cpuTimeTestUtils.buildModelFromSpec; |
+ const getSimpleModelSpec = tr.e.chrome.cpuTimeTestUtils.getSimpleModelSpec; |
+ |
+ // A very small epsilon to assert with acceptable precision the correctness of |
+ // small floating point values produced by cpu time metric. |
+ const EPSILON7 = 1e-7; |
+ |
+ // Shorthand for initiator types |
+ const IT = tr.model.um.INITIATOR_TYPE; |
+ |
+ function computeMetricValue(modelSpec, metricName, opt_rangeOfInterest) { |
+ const model = buildModelFromSpec(modelSpec); |
const histograms = new tr.v.HistogramSet(); |
- tr.metrics.sh.cpuTimeMetric(histograms, model, opt_options); |
- return tr.b.getOnlyElement(histograms).average; |
+ const options = opt_rangeOfInterest ? |
+ {rangeOfInterest: opt_rangeOfInterest} : undefined; |
+ tr.metrics.sh.cpuTimeMetric(histograms, model, options); |
+ return histograms.getHistogramNamed(metricName).sum; |
} |
- // There are two slices, each of length 50. The total bounds is 3000. |
- // This yields total CPU time of 100ms, averaged over 3 seconds is 33ms. |
- test('cpuTimeMetric_oneProcess', function() { |
- const sliceDuration = 50; |
- const totalDuration = 3000; |
- const value = computeCpuTime(function(model) { |
- model.rendererProcess = model.getOrCreateProcess(2); |
- model.rendererMain = model.rendererProcess.getOrCreateThread(3); |
- model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: 0, |
- duration: sliceDuration, |
- cpuStart: 0, |
- cpuDuration: sliceDuration, |
- })); |
- model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: totalDuration - sliceDuration, |
- duration: sliceDuration, |
- cpuStart: totalDuration - sliceDuration, |
- cpuDuration: sliceDuration, |
- })); |
- }); |
- assert.closeTo(value, 0.033, 0.001); |
+ function assertMetricValueInHistogram(histograms, metricName, expectedValue) { |
+ const value = histograms.getHistogramNamed(metricName).sum; |
+ assert.closeTo(value, expectedValue, EPSILON7); |
+ } |
+ |
+ // TODO: DO NOT COMMIT THIS. Fix all instances. This is a temporary |
+ // workaround while I get the higher level structure of the code sorted out. |
+ function fixMetricName(name) { |
+ const parts = name.split(':'); |
+ if (parts[1] === 'GPU') parts[1] = 'gpu_process'; |
+ parts[1] = tr.e.chrome.chrome_processes.canonicalizeProcessName(parts[1]); |
+ parts[0] = "cpuUsage"; |
+ return parts.join(":"); |
+ } |
+ |
+ test('slicesDoNotStraddleExpecationBoundaries', () => { |
+ const simpleModelSpec = getSimpleModelSpec(); |
+ simpleModelSpec.processes[0].threads[0].slices.push( |
+ {range: [150, 200], cpu: 30}, |
+ {range: [205, 255], cpu: 20} |
+ ) |
+ simpleModelSpec.expectations.push( |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [100, 300]} |
+ ) |
+ const metricName = `cpuUsage:${CHROME_PROCESS_NAMES.BROWSER}:` + |
+ `CrBrowserMain:Animation:CSS`; |
+ const value = computeMetricValue(simpleModelSpec, metricName); |
+ assert.closeTo(value, (30 + 20) / 200, EPSILON7); |
+ }); |
+ |
+ test('slicesStraddleExpectationBoundaries', () => { |
+ const simpleModelSpec = getSimpleModelSpec(); |
+ simpleModelSpec.processes[0].threads[0].slices.push( |
+ {range: [150, 200], cpu: 30}, |
+ {range: [205, 255], cpu: 20} |
+ ) |
+ simpleModelSpec.expectations.push( |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [75, 175]} |
+ ) |
+ const metricName = `cpuUsage:${CHROME_PROCESS_NAMES.BROWSER}:` + |
+ `CrBrowserMain:Animation:Video`; |
+ const value = computeMetricValue(simpleModelSpec, metricName); |
+ assert.closeTo(value, 15 / 100, EPSILON7); |
+ }); |
+ |
+ test('singleThread-disjointExpectationsOfSameInitiator', () => { |
+ const simpleModelSpec = getSimpleModelSpec(); |
+ simpleModelSpec.processes[0].threads[0].slices.push( |
+ {range: [150, 200], cpu: 30}, |
+ {range: [205, 255], cpu: 20} |
+ ) |
+ simpleModelSpec.expectations.push( |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [100, 160]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [205, 225]} |
+ ) |
+ const metricName = `cpuUsage:${CHROME_PROCESS_NAMES.BROWSER}` + |
+ `:CrBrowserMain:Response:Scroll`; |
+ const value = computeMetricValue(simpleModelSpec, metricName); |
+ assert.closeTo(value, (0.2 * 30 + 0.4 * 20) / (60 + 20), EPSILON7); |
+ }); |
+ |
+ test('singleThread-overlappingExpectationsOfSameInitiator', () => { |
+ const simpleModelSpec = getSimpleModelSpec(); |
+ simpleModelSpec.processes[0].threads[0].slices.push( |
+ {range: [150, 200], cpu: 30}, |
+ {range: [205, 255], cpu: 20} |
+ ) |
+ simpleModelSpec.expectations.push( |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [100, 190]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [170, 230]} |
+ ) |
+ const metricName = fixMetricName('cpuTime:Browser:CrBrowserMain:Response:Scroll'); |
+ const value = computeMetricValue(simpleModelSpec, metricName); |
+ assert.closeTo(value, (30 + .5 * 20) / 130, EPSILON7); |
+ }); |
+ |
+ test('singleThread-disjointExpectationsOfDifferentInitiators', () => { |
+ const simpleModelSpec = getSimpleModelSpec(); |
+ simpleModelSpec.processes[0].threads[0].slices.push( |
+ {range: [150, 200], cpu: 30}, |
+ {range: [205, 255], cpu: 20} |
+ ) |
+ simpleModelSpec.expectations.push( |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [100, 160]}, |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [205, 230]} |
+ ); |
+ const metricName = fixMetricName('cpuTime:Browser:CrBrowserMain:Animation:all_initiators'); |
+ const value = computeMetricValue(simpleModelSpec, metricName); |
+ assert.closeTo(value, (0.2 * 30 + 0.5 * 20) / 85, EPSILON7); |
+ }); |
+ |
+ test('singleThread-overlappingExpectationsOfDifferentInitiators', () => { |
+ const simpleModelSpec = getSimpleModelSpec(); |
+ simpleModelSpec.processes[0].threads[0].slices.push( |
+ {range: [150, 200], cpu: 30}, |
+ {range: [205, 255], cpu: 20} |
+ ) |
+ simpleModelSpec.expectations.push( |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [100, 190]}, |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [160, 230]} |
+ ); |
+ const metricName = fixMetricName('cpuTime:Browser:CrBrowserMain:Animation:all_initiators'); |
+ const value = computeMetricValue(simpleModelSpec, metricName); |
+ assert.closeTo(value, (30 + 0.5 * 20) / 130, EPSILON7); |
}); |
- // Normalize against the browser process, whose non-CPU slice goes from 2900 |
- // to 3000. |
- test('cpuTimeMetric_browserProcess', function() { |
- const sliceDuration = 50; |
- const totalDuration = 3000; |
- const model = tr.e.chrome.ChromeTestUtils.newChromeModel(function(model) { |
- model.browserMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: totalDuration - 2 * sliceDuration, |
- duration: 2 * sliceDuration, |
- })); |
- model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: 0, |
- duration: sliceDuration, |
- cpuStart: 0, |
- cpuDuration: sliceDuration, |
- })); |
- model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: totalDuration - sliceDuration, |
- duration: sliceDuration, |
- cpuStart: totalDuration - sliceDuration, |
- cpuDuration: sliceDuration, |
- })); |
- }); |
+ test('singleThread-allStages-customRangeOfInterest', () => { |
+ const simpleModelSpec = getSimpleModelSpec(); |
+ simpleModelSpec.processes[0].threads[0].slices.push( |
+ {range: [150, 200], cpu: 30}, |
+ {range: [205, 255], cpu: 20} |
+ ) |
+ simpleModelSpec.expectations.push( |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [100, 190]}, |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [160, 230]} |
+ ); |
+ const metricName = fixMetricName('cpuTime:Browser:CrBrowserMain:all_stages:all_initiators'); |
+ const rangeOfInterest = new tr.b.math.Range.fromExplicitRange(100, 210); |
+ const value = computeMetricValue( |
+ simpleModelSpec, metricName, rangeOfInterest); |
+ assert.closeTo(value, (30 + 0.1 * 20)/ 110, EPSILON7); |
+ }); |
+ |
+ test('singleThread-allStages-defaultRangeOfInterest', () => { |
+ const simpleModelSpec = getSimpleModelSpec(); |
+ simpleModelSpec.processes[0].threads[0].slices.push( |
+ {range: [150, 200], cpu: 30}, |
+ {range: [205, 255], cpu: 20} |
+ ) |
+ simpleModelSpec.expectations.push( |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [160, 230]} |
+ ); |
+ const metricName = fixMetricName('cpuTime:Browser:CrBrowserMain:all_stages:all_initiators'); |
+ const value = computeMetricValue(simpleModelSpec, metricName); |
+ assert.closeTo(value, 50 / 105, EPSILON7); |
+ }); |
+ |
+ test('multipleThreadsOfSameTypeAggregate', () => { |
+ const modelSpec = { |
+ processes: [ |
+ { |
+ name: 'Browser', |
+ pid: 12345, |
+ threads: [ |
+ { |
+ name: 'CrBrowserMain', |
+ tid: 1, |
+ slices: [], |
+ }, |
+ { |
+ name: 'Worker/1', |
+ tid: 42, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 20) { |
+ slices.push({range:[i, i + 10], cpu: 5}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ { |
+ name: 'Worker/2', |
+ tid: 52, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 100) { |
+ slices.push({range:[i, i + 80], cpu: 40}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ ], |
+ }, |
+ ], |
+ |
+ // [Video A] [Click R] [CSS A] [ CSS A ] |
+ // [Scroll R] |
+ // [Scroll R] |
+ expectations: [ |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [0, 90]}, |
+ |
+ {stage: 'Response', initiatorType: IT.CLICK, range: [200, 220]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [210, 260]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [250, 300]}, |
+ |
+ |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [500, 560]}, |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [690, 890]}, |
+ ], |
+ }; |
+ |
+ const model = buildModelFromSpec(modelSpec); |
const histograms = new tr.v.HistogramSet(); |
tr.metrics.sh.cpuTimeMetric(histograms, model); |
- const value = tr.b.getOnlyElement(histograms).average; |
- assert.closeTo(value, 0.5, 0.001); |
+ |
+ // Single expectation range of same initiator type. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Browser:Worker:Animation:Video'), |
+ (5 * 5 + 40) / 90); |
+ |
+ // Disjoint expectation ranges of same initiator type. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Browser:Worker:Animation:CSS'), |
+ ((5 * 3 + 40 * (60 / 80)) + (5 * 10 + 40 * 2)) / (60 + 200)); |
+ |
+ // Overlapping expectation ranges of same initiator type. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Browser:Worker:Response:Scroll'), |
+ (5 * 4 + 40 * (70 / 80))/ 90); |
+ |
+ // Disjoint expectation ranges with all_initiators. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Browser:Worker:Animation:all_initiators'), |
+ ((5 * 5 + 40) |
+ + ((5 * 3 + 40 * (60 / 80)) + (5 * 10 + 40 * 2))) |
+ / (90 + 60 + 200)); |
+ |
+ // Overlapping expectation ranges with all_initiators. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Browser:Worker:Response:all_initiators'), |
+ (5 * 5 + 40) / 100); |
+ |
+ // all_stages and all_initiators. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Browser:Worker:all_stages:all_initiators'), |
+ (250 + 400) / 990); |
}); |
- // Makes sure that rangeOfInterest works correctly. |
- test('cpuTimeMetric_oneProcess_rangeOfInterest', function() { |
- const sliceDuration = 50; |
- const totalDuration = 3000; |
- const rangeOfInterest = new tr.b.math.Range.fromExplicitRange(-10, 30); |
- const options = {}; |
- options.rangeOfInterest = rangeOfInterest; |
- const value = computeCpuTime(function(model) { |
- model.rendererProcess = model.getOrCreateProcess(2); |
- model.rendererMain = model.rendererProcess.getOrCreateThread(3); |
- model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: 0, |
- duration: sliceDuration, |
- cpuStart: 0, |
- cpuDuration: sliceDuration, |
- })); |
- model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: totalDuration - sliceDuration, |
- duration: sliceDuration, |
- cpuStart: totalDuration - sliceDuration, |
- cpuDuration: sliceDuration, |
- })); |
- }, options); |
- assert.closeTo(value, 0.75, 0.001); |
+ test('multipleProcessesOfSameTypeAggregate', () => { |
+ const modelSpec = { |
+ processes: [ |
+ { |
+ name: 'Browser', |
+ pid: 12345, |
+ threads: [ |
+ { |
+ name: 'CrBrowserMain', |
+ tid: 1, |
+ slices: [], |
+ }, |
+ ], |
+ }, |
+ { |
+ name: 'Renderer', |
+ pid: 20001, |
+ threads: [ |
+ { |
+ name: 'CrRendererMain', |
+ tid: 42, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 20) { |
+ slices.push({range:[i, i + 10], cpu: 5}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ ], |
+ }, |
+ { |
+ name: 'Renderer', |
+ pid: 30001, |
+ threads: [ |
+ { |
+ name: 'CrRendererMain', |
+ tid: 52, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 100) { |
+ slices.push({range:[i, i + 80], cpu: 40}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ ] |
+ }, |
+ ], |
+ |
+ // [Video A] [Click R] [CSS A] [ CSS A ] |
+ // [Scroll R] |
+ // [Scroll R] |
+ expectations: [ |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [0, 90]}, |
+ |
+ {stage: 'Response', initiatorType: IT.CLICK, range: [200, 220]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [210, 260]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [250, 300]}, |
+ |
+ |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [500, 560]}, |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [690, 890]}, |
+ ], |
+ }; |
+ |
+ const model = buildModelFromSpec(modelSpec); |
+ const histograms = new tr.v.HistogramSet(); |
+ tr.metrics.sh.cpuTimeMetric(histograms, model); |
+ |
+ // Single expectation range of same initiator type. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Renderer:CrRendererMain:Animation:Video'), |
+ (5 * 5 + 40) / 90); |
+ |
+ // Disjoint expectation ranges of same initiator type. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Renderer:CrRendererMain:Animation:CSS'), |
+ ((5 * 3 + 40 * (60 / 80)) + (5 * 10 + 40 * 2)) / (60 + 200)); |
+ |
+ // Overlapping expectation ranges of same initiator type. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Renderer:CrRendererMain:Response:Scroll'), |
+ (5 * 4 + 40 * (70 / 80))/ 90); |
+ |
+ // Disjoint expectation ranges with all_initiators. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Renderer:CrRendererMain:Animation:all_initiators'), |
+ ((5 * 5 + 40) |
+ + ((5 * 3 + 40 * (60 / 80)) + (5 * 10 + 40 * 2))) |
+ / (90 + 60 + 200)); |
+ |
+ // Overlapping expectation ranges with all_initiators. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Renderer:CrRendererMain:Response:all_initiators'), |
+ (5 * 5 + 40) / 100); |
+ |
+ // all_stages and all_initiators. |
+ assertMetricValueInHistogram(histograms, |
+ fixMetricName('cpuTime:Renderer:CrRendererMain:all_stages:all_initiators'), |
+ (250 + 400) / 990); |
+ }); |
+ |
+ test('allThreadsOfSameProcessAggregate', () => { |
+ const modelSpec = { |
+ processes: [ |
+ { |
+ name: 'Browser', |
+ pid: 12345, |
+ threads: [ |
+ { |
+ name: 'CrBrowserMain', |
+ tid: 1, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 20) { |
+ slices.push({range:[i, i + 10], cpu: 5}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ { |
+ name: 'Chrome_IOThread', |
+ tid: 5, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 100) { |
+ slices.push({range:[i, i + 80], cpu: 40}); |
+ } |
+ return slices; |
+ })(), |
+ } |
+ ], |
+ }, |
+ ], |
+ |
+ // [Video A] [Click R] [CSS A] [ CSS A ] |
+ // [Scroll R] |
+ // [Scroll R] |
+ expectations: [ |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [0, 90]}, |
+ |
+ {stage: 'Response', initiatorType: IT.CLICK, range: [200, 220]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [210, 260]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [250, 300]}, |
+ |
+ |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [500, 560]}, |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [690, 890]}, |
+ ], |
+ }; |
+ |
+ const model = buildModelFromSpec(modelSpec); |
+ const histograms = new tr.v.HistogramSet(); |
+ tr.metrics.sh.cpuTimeMetric(histograms, model); |
+ |
+ const subMetricPrefix1 = |
+ 'cpuTime:Browser:CrBrowserMain'; |
+ const subMetricPrefix2 = |
+ 'cpuTime:Browser:Chrome_IOThread'; |
+ const aggregateMetricPrefix = |
+ 'cpuTime:Browser:all_threads'; |
+ |
+ function getMetricValueFromHistogram(histograms, metricName) { |
+ return histograms.getHistogramNamed(fixMetricName(metricName)).sum; |
+ } |
+ |
+ const metricSuffixes = [ |
+ // Single expectation range of same initiator type. |
+ 'Animation:Video', |
+ // Disjoint expectation ranges of same initiator type. |
+ 'Animation:CSS', |
+ // Overlapping expectation ranges of same initiator type. |
+ 'Response:Scroll', |
+ // Disjoint expectation ranges with all_initiators. |
+ 'Animation:all_initiators', |
+ // Overlapping expectation ranges with all_initiators. |
+ 'Response:all_initiators', |
+ // all_stages and all_initiators. |
+ 'all_stages:all_initiators', |
+ ] |
+ |
+ for (const suffix of metricSuffixes) { |
+ const subMetric1 = getMetricValueFromHistogram(histograms, |
+ `${subMetricPrefix1}:${suffix}`); |
+ const subMetric2 = getMetricValueFromHistogram(histograms, |
+ `${subMetricPrefix2}:${suffix}`); |
+ const aggregateMetric = getMetricValueFromHistogram(histograms, |
+ `${aggregateMetricPrefix}:${suffix}`); |
+ assert.closeTo(aggregateMetric, subMetric1 + subMetric2, EPSILON7); |
+ } |
}); |
- // Process 1: There are two slices, each of length 50. The total bounds is |
- // 3000. Process 2: There is one slice of length 50. |
- // This yields total CPU time of 150ms, averaged over 3 seconds is 50ms. |
- test('cpuTimeMetric_twoProcesses', function() { |
- const sliceDuration = 50; |
- const totalDuration = 3000; |
- const value = computeCpuTime(function(model) { |
- model.rendererProcess = model.getOrCreateProcess(2); |
- model.rendererMain = model.rendererProcess.getOrCreateThread(3); |
- model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: 0, |
- duration: sliceDuration, |
- cpuStart: 0, |
- cpuDuration: sliceDuration, |
- })); |
- model.rendererMain.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: totalDuration - sliceDuration, |
- duration: sliceDuration, |
- cpuStart: totalDuration - sliceDuration, |
- cpuDuration: sliceDuration, |
- })); |
- |
- const otherProcess = model.getOrCreateProcess(3); |
- const otherThread = otherProcess.getOrCreateThread(4); |
- otherThread.sliceGroup.pushSlice(tr.c.TestUtils.newSliceEx({ |
- type: tr.model.ThreadSlice, |
- isTopLevel: true, |
- start: 0, |
- duration: sliceDuration, |
- cpuStart: 0, |
- cpuDuration: sliceDuration, |
- })); |
- }); |
- assert.closeTo(value, 0.05, 0.001); |
+ test('threadsAcrossMultipleProcessTypesAggregate', () => { |
+ const modelSpec = { |
+ processes: [ |
+ { |
+ name: 'Browser', |
+ pid: 12345, |
+ threads: [ |
+ { |
+ name: 'CrBrowserMain', |
+ tid: 1, |
+ slices: [], |
+ }, |
+ ], |
+ }, |
+ { |
+ name: 'Renderer', |
+ pid: 20001, |
+ threads: [ |
+ { |
+ name: 'Chrome_ChildIOThread', |
+ tid: 42, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 20) { |
+ slices.push({range:[i, i + 10], cpu: 5}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ ], |
+ }, |
+ { |
+ name: 'GPU Process', |
+ pid: 30001, |
+ threads: [ |
+ { |
+ name: 'Chrome_ChildIOThread', |
+ tid: 52, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 100) { |
+ slices.push({range:[i, i + 80], cpu: 40}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ ] |
+ }, |
+ ], |
+ |
+ // [Video A] [Click R] [CSS A] [ CSS A ] |
+ // [Scroll R] |
+ // [Scroll R] |
+ expectations: [ |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [0, 90]}, |
+ |
+ {stage: 'Response', initiatorType: IT.CLICK, range: [200, 220]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [210, 260]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [250, 300]}, |
+ |
+ |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [500, 560]}, |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [690, 890]}, |
+ ], |
+ }; |
+ |
+ const model = buildModelFromSpec(modelSpec); |
+ const histograms = new tr.v.HistogramSet(); |
+ tr.metrics.sh.cpuTimeMetric(histograms, model); |
+ |
+ const subMetricPrefix1 = |
+ 'cpuTime:Renderer:Chrome_ChildIOThread'; |
+ const subMetricPrefix2 = |
+ 'cpuTime:GPU:Chrome_ChildIOThread'; |
+ const aggregateMetricPrefix = |
+ 'cpuTime:all_processes:Chrome_ChildIOThread'; |
+ |
+ function getMetricValueFromHistogram(histograms, metricName) { |
+ return histograms.getHistogramNamed(fixMetricName(metricName)).sum; |
+ } |
+ |
+ const metricSuffixes = [ |
+ // Single expectation range of same initiator type. |
+ 'Animation:Video', |
+ // Disjoint expectation ranges of same initiator type. |
+ 'Animation:CSS', |
+ // Overlapping expectation ranges of same initiator type. |
+ 'Response:Scroll', |
+ // Disjoint expectation ranges with all_initiators. |
+ 'Animation:all_initiators', |
+ // Overlapping expectation ranges with all_initiators. |
+ 'Response:all_initiators', |
+ // all_stages and all_initiators. |
+ 'all_stages:all_initiators', |
+ ] |
+ |
+ for (const suffix of metricSuffixes) { |
+ const subMetricName1 = `${subMetricPrefix1}:${suffix}`; |
+ const subMetricName2 = `${subMetricPrefix2}:${suffix}`; |
+ const aggregateMetricName = `${aggregateMetricPrefix}:${suffix}`; |
+ const subMetric1 = getMetricValueFromHistogram(histograms, subMetricName1); |
+ const subMetric2 = getMetricValueFromHistogram(histograms, subMetricName2); |
+ const aggregateMetric = getMetricValueFromHistogram(histograms, |
+ aggregateMetricName); |
+ assert.closeTo(aggregateMetric, subMetric1 + subMetric2, EPSILON7, |
+ `${subMetricName1} and ${subMetricName2} aggregates to ` + |
+ aggregateMetricName); |
+ } |
+ }); |
+ |
+ test('completeAggregation', () => { |
+ const modelSpec = { |
+ processes: [ |
+ { |
+ name: 'Browser', |
+ pid: 12345, |
+ threads: [ |
+ { |
+ name: 'CrBrowserMain', |
+ tid: 1, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 50) { |
+ slices.push({range:[i, i + 50], cpu: 30}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ ], |
+ }, |
+ { |
+ name: 'Renderer', |
+ pid: 20001, |
+ threads: [ |
+ { |
+ name: 'Chrome_ChildIOThread', |
+ tid: 42, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 20) { |
+ slices.push({range:[i, i + 10], cpu: 5}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ ], |
+ }, |
+ { |
+ name: 'GPU Process', |
+ pid: 30001, |
+ threads: [ |
+ { |
+ name: 'Chrome_ChildIOThread', |
+ tid: 52, |
+ slices: (() => { |
+ const slices = []; |
+ for (let i = 0; i < 1000; i += 100) { |
+ slices.push({range:[i, i + 80], cpu: 40}); |
+ } |
+ return slices; |
+ })(), |
+ }, |
+ ] |
+ }, |
+ ], |
+ |
+ // [Video A] [Click R] [CSS A] [ CSS A ] |
+ // [Scroll R] |
+ // [Scroll R] |
+ expectations: [ |
+ {stage: 'Animation', initiatorType: IT.VIDEO, range: [0, 90]}, |
+ |
+ {stage: 'Response', initiatorType: IT.CLICK, range: [200, 220]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [210, 260]}, |
+ {stage: 'Response', initiatorType: IT.SCROLL, range: [250, 300]}, |
+ |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [500, 560]}, |
+ {stage: 'Animation', initiatorType: IT.CSS, range: [690, 890]}, |
+ ], |
+ }; |
+ |
+ const model = buildModelFromSpec(modelSpec); |
+ const histograms = new tr.v.HistogramSet(); |
+ tr.metrics.sh.cpuTimeMetric(histograms, model); |
+ |
+ const metricName = |
+ fixMetricName('cpuTime:all_processes:all_threads:all_stages:all_initiators'); |
+ const histogram = histograms.getHistogramNamed(metricName); |
+ const metricValue = histogram.sum; |
+ |
+ assert.closeTo(metricValue, (30 * 20 + 5 * 50 + 40 * 10) / 1000, EPSILON7); |
}); |
}); |
</script> |