<!DOCTYPE HTML> <html> <!-- Copyright (c) 2012 The Chromium Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file. --> <head i18n-values="dir:textdirection;"> <title>TimelineAnalysis tests</title> <script src="base.js"></script> <style> .timeline-view { border: 1px solid black; margin: 10px; } .timeline-find-dialog { border: 1px solid black; margin: 10px; } </style> </head> <body> <script> base.require('unittest'); base.require('tracing_controller'); base.require('test_utils'); base.require('timeline_model'); base.require('trace_event_importer'); base.require('timeline_analysis'); base.require('timeline_filter'); base.require('tracks.timeline_counter_track'); base.require('tracks.timeline_slice_track'); base.require('tracks.timeline_thread_track'); base.require('timeline'); /* TODO(nduca): reduce dependency */ </script> <script> 'use strict'; var TimelineCounter = tracing.TimelineCounter; var TimelineThread = tracing.TimelineThread; var TimelineAnalysisView = tracing.TimelineAnalysisView; var TimelineModel = tracing.TimelineModel; var TimelineThreadTrack = tracks.TimelineThreadTrack; var TimelineSelection = tracing.TimelineSelection; var TimelineTitleFilter = tracing.TimelineTitleFilter; var TimelineCounterTrack = tracks.TimelineCounterTrack; var TimelineSliceTrack = tracks.TimelineSliceTrack; var newSliceNamed = test_utils.newSliceNamed; var newSliceCategory = test_utils.newSliceCategory; function createSelectionWithSingleSlice(withCategory) { var model = new TimelineModel(); var t53 = model.getOrCreateProcess(52).getOrCreateThread(53); if (withCategory) t53.pushSlice(newSliceCategory('foo', 'b', 0, 0.002)); else t53.pushSlice(newSliceNamed('b', 0, 0.002)); var t53track = new TimelineThreadTrack(); t53track.thread = t53; var selection = new TimelineSelection(); t53track.addAllObjectsMatchingFilterToSelection( new TimelineTitleFilter('b'), selection); assertEquals(1, selection.length); return selection; } function createSelectionWithTwoSlices() { var events = [ {name: 'a', args: {}, pid: 52, ts: 520, cat: 'foo', tid: 53, ph: 'B'}, {name: 'a', args: {}, pid: 52, ts: 560, cat: 'foo', tid: 53, ph: 'E'}, {name: 'aa', args: {}, pid: 52, ts: 640, cat: 'foo', tid: 53, ph: 'B'}, {name: 'aa', args: {}, pid: 52, ts: 700, cat: 'foo', tid: 53, ph: 'E'} ]; var model = new TimelineModel(events); var t53track = new TimelineThreadTrack(); t53track.thread = model.processes[52].threads[53]; var selection = new TimelineSelection(); t53track.addAllObjectsMatchingFilterToSelection( new TimelineTitleFilter('a'), selection); assertEquals(2, selection.length); return selection; } function createSelectionWithTwoSlicesSameTitle() { var events = [ {name: 'c', args: {}, pid: 52, ts: 620, cat: 'foo', tid: 53, ph: 'B'}, {name: 'c', args: {}, pid: 52, ts: 660, cat: 'foo', tid: 53, ph: 'E'}, {name: 'c', args: {}, pid: 52, ts: 740, cat: 'foo', tid: 53, ph: 'B'}, {name: 'c', args: {}, pid: 52, ts: 800, cat: 'foo', tid: 53, ph: 'E'} ]; var model = new TimelineModel(events); var t53track = new TimelineThreadTrack(); t53track.thread = model.processes[52].threads[53]; var selection = new TimelineSelection(); t53track.addAllObjectsMatchingFilterToSelection( new TimelineTitleFilter('c'), selection); assertEquals(2, selection.length); return selection; } function createSelectionWithCounters(numSamples) { if (numSamples > 2 || numSamples < 1) throw new Error('This function only supports 1 or 2 samples'); var events = [ {name: 'ctr', args: {'value': 0}, pid: 1, ts: 0, cat: 'foo', tid: 1, ph: 'C', id: 0}, {name: 'ctr', args: {'value': 10}, pid: 1, ts: 10, cat: 'foo', tid: 1, ph: 'C', id: 0} ]; var model = new TimelineModel(events); var p = model.processes[1]; var ctr = model.processes[1].counters['foo.ctr[0]']; assertEquals('ctr[0]', ctr.name); assertEquals(2, ctr.numSamples); assertEquals(1, ctr.numSeries); assertArrayEquals([0, 0.01], ctr.timestamps); assertArrayEquals([0, 10], ctr.samples); var selection = new TimelineSelection(); var t1track = new TimelineThreadTrack(); selection.addCounterSample(t1track, ctr, 1); if (numSamples == 1) return selection; selection.addCounterSample(t1track, ctr, 0); return selection; } function createSelectionWithTwoSeriesSingleCounter() { var events = [ {name: 'ctr', args: {'bytesallocated': 0, 'bytesfree': 25}, pid: 1, ts: 0, cat: 'foo', tid: 1, ph: 'C', id: 0}, {name: 'ctr', args: {'bytesallocated': 10, 'bytesfree': 15}, pid: 1, ts: 10, cat: 'foo', tid: 1, ph: 'C', id: 0}, {name: 'ctr', args: {'bytesallocated': 20, 'bytesfree': 5}, pid: 1, ts: 20, cat: 'foo', tid: 1, ph: 'C', id: 0} ]; var model = new TimelineModel(events); var p = model.processes[1]; var ctr = model.processes[1].counters['foo.ctr[0]']; assertEquals('ctr[0]', ctr.name); assertEquals(3, ctr.numSamples); assertEquals(2, ctr.numSeries); var selection = new TimelineSelection(); var t1track = new TimelineThreadTrack(); selection.addCounterSample(t1track, ctr, 1); return selection; } function createSelectionWithTwoSeriesTwoCounters() { var ctr1 = new TimelineCounter(null, 0, '', 'ctr1'); ctr1.seriesNames.push('bytesallocated', 'bytesfree'); ctr1.seriesColors.push(0, 1); ctr1.timestamps.push(0, 10, 20); ctr1.samples.push(0, 25, 10, 15, 20, 5); var ctr2 = new TimelineCounter(null, 0, '', 'ctr2'); ctr2.seriesNames.push('bytesallocated', 'bytesfree'); ctr2.seriesColors.push(0, 1); ctr2.timestamps.push(0, 10, 20); ctr2.samples.push(0, 25, 10, 15, 20, 5); var selection = new TimelineSelection(); var t1track = new TimelineThreadTrack(); selection.addCounterSample(t1track, ctr1, 1); selection.addCounterSample(t1track, ctr2, 2); return selection; } function createSelectionWithTwoCountersDiffSeriesDiffHits() { var ctr1 = new TimelineCounter(null, 0, '', 'a'); ctr1.seriesNames.push('bytesallocated'); ctr1.seriesColors.push(0); ctr1.timestamps.push(0, 10, 20); ctr1.samples.push(0, 25, 10); assertEquals('a', ctr1.name); assertEquals(3, ctr1.numSamples); assertEquals(1, ctr1.numSeries); var ctr2 = new TimelineCounter(null, 0, '', 'b'); ctr2.seriesNames.push('bytesallocated', 'bytesfree'); ctr2.seriesColors.push(0, 1); ctr2.timestamps.push(0, 10, 20, 30); ctr2.samples.push(0, 25, 10, 15, 20, 5, 25, 0); assertEquals('b', ctr2.name); assertEquals(4, ctr2.numSamples); assertEquals(2, ctr2.numSeries); var selection = new TimelineSelection(); var t1track = new TimelineThreadTrack(); selection.addCounterSample(t1track, ctr1, 1); selection.addCounterSample(t1track, ctr2, 2); return selection; } function createSelectionWithSingleSliceSingleCounter() { var model = new TimelineModel(); var thread = model.getOrCreateProcess(1).getOrCreateThread(1); thread.pushSlice(newSliceNamed('b', 1, 5)); var ctr1 = model.getOrCreateProcess(1).getOrCreateCounter('cat', 'ctr1'); ctr1.seriesNames.push('bytesallocated', 'bytesfree'); ctr1.seriesColors.push(0, 1); ctr1.timestamps.push(0, 10, 20); ctr1.samples.push(0, 25, 10, 15, 20, 5); assertEquals('ctr1', ctr1.name); assertEquals(3, ctr1.numSamples); assertEquals(2, ctr1.numSeries); var ctr1track = new TimelineCounterTrack(); ctr1track.counter = ctr1; var threadTrack = new TimelineSliceTrack(); threadTrack.slices = thread.slices; var selection = new TimelineSelection(); selection.addCounterSample(ctr1track, ctr1, 1); threadTrack.addAllObjectsMatchingFilterToSelection( new TimelineTitleFilter('b'), selection); assertEquals(2, selection.length); return selection; } function createSelectionWithNormallyDistributedSamples(numSlices) { // Distance between start times is normally distributed, with mu = 16ms // and sigma = 3ms. var startTimes = [ 0, 18.4362262859, 32.5378088645, 44.8978868054, 63.4772725504, 77.438888345, 92.0102867913, 99.6208686689, 119.150576393, 137.54545468, 153.991587743, 175.456095568, 193.395772651, 205.691644582, 218.740054982, 239.308480724, 250.880949151, 268.528689601, 281.950478133, 296.791635722, 315.862427391, 333.954888221, 342.392899581, 362.364373939, 377.593380892, 392.296896748, 415.779941407, 435.517713864, 454.581222491, 470.329018858, 488.37029095, 502.283017166, 521.15141113, 534.36224697, 554.425018316, 574.89913248, 589.60294439, 604.780562233, 615.481610668, 630.055628965, 645.908449096, 661.776084055, 673.276049017, 689.776401428, 704.440135004, 716.33262401, 732.380086528, 743.970715322, 756.506690025, 772.391485532, 794.636984401, 803.801415494, 819.006502926, 837.610127549, 854.551103283, 875.170613672, 891.508235124, 905.263299017, 929.309555683, 943.417968804, 957.289319239, 972.302910569, 986.669355637, 1002.71558868, 1013.83359637, 1030.16840733, 1040.39503139, 1057.61583325, 1075.64709686, 1086.67671319, 1100.4617455, 1118.4871842, 1129.98143488, 1144.52318588, 1160.36966285, 1179.50049042, 1195.03088169, 1215.98199401, 1226.66591838, 1245.83650314, 1268.18058265, 1285.11047342, 1301.71570575, 1316.40723345, 1329.94342488, 1343.7569577, 1358.28267513, 1371.17560308, 1386.42247119, 1401.51767749, 1417.52489051, 1440.98712348, 1457.80113781, 1475.66079406, 1494.64137536, 1509.52941903, 1524.54762552, 1545.42960714, 1565.19444597, 1580.56308936, 1596.72211651]; var events = []; var model = new TimelineModel(); var thread = model.getOrCreateProcess(52).getOrCreateThread(53); var duration = 1; // 1ms for (var i = 0; i < startTimes.length; ++i) { for (var j = 0; j < numSlices; ++j) { var name = 'slice' + String(numSlices - 1 - j); thread.slices.push(newSliceNamed(name, startTimes[i], duration)); } } var t53track = new TimelineThreadTrack(); t53track.thread = model.processes[52].threads[53]; var selection = new TimelineSelection(); t53track.addAllObjectsMatchingFilterToSelection( new TimelineTitleFilter('slice'), selection); assertEquals(101 * numSlices, selection.length); return selection; } function testAnalysisViewWithSingleSlice() { var selection = createSelectionWithSingleSlice(); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); } function testAnalysisViewWithSingleSliceCategory() { var selection = createSelectionWithSingleSlice(true); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); } function testAnalysisViewWithMultipleSlices() { var selection = createSelectionWithTwoSlices(); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); } function testAnalysisViewWithMultipleSlicesSameTitle() { var selection = createSelectionWithTwoSlicesSameTitle(); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); } function testAnalysisViewWithSingleCounterWithTwoSeries() { var selection = createSelectionWithTwoSeriesSingleCounter(); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); } function testAnalysisViewWithTwoCountersWithTwoSeries() { var selection = createSelectionWithTwoSeriesTwoCounters(); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); } function testAnalysisViewWithSingleSliceSingleCounter() { var selection = createSelectionWithSingleSliceSingleCounter(); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); } function testSelectionWithNormallyDistributedSamples() { var numSlices = 1; var selection = createSelectionWithNormallyDistributedSamples(numSlices); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); } function StubAnalysisResults() { this.tables = []; } StubAnalysisResults.prototype = { __proto__: Object.protoype, appendTable: function(parent, className) { var table = { className: className, rows: [] }; table.className = className; this.tables.push(table); return table; }, appendTableHeader: function(table, label) { if (table.tableHeader) throw new Error('Only one summary header allowed.'); table.tableHeader = label; }, appendSummaryRow: function(table, label, opt_text) { table.rows.push({label: label, text: opt_text}); }, appendSpacingRow: function(table) { table.rows.push({spacing: true}); }, appendSummaryRowTime: function(table, label, time) { table.rows.push({label: label, time: time}); }, appendDataRow: function(table, label, duration, occurences, details) { table.rows.push({label: label, duration: duration, occurences: occurences, details: details}); } }; function testAnalyzeSelectionWithSingleSlice() { var selection = createSelectionWithSingleSlice(); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(1, results.tables.length); var table = results.tables[0]; assertEquals('Selected slice:', table.tableHeader); assertEquals(3, table.rows.length); assertEquals('b', table.rows[0].text); assertEquals(0, table.rows[1].time); assertAlmostEquals(0.002, table.rows[2].time); } function testAnalyzeSelectionWithFalsyArgs() { var model = new TimelineModel(); var t53 = model.getOrCreateProcess(52).getOrCreateThread(53); var slice = newSliceNamed('b', 0, 0.002); slice.args.bar = 0; slice.args.foo = false; t53.pushSlice(slice); var t53track = new TimelineThreadTrack(); t53track.thread = t53; var selection = new TimelineSelection(); t53track.addAllObjectsMatchingFilterToSelection( new TimelineTitleFilter('b'), selection); assertEquals(1, selection.length); var analysisEl = new TimelineAnalysisView(); analysisEl.selection = selection; this.addHTMLOutput(undefined, analysisEl); var rows = analysisEl.querySelectorAll('tr'); assertEquals(rows.length, 7); assertEquals(' bar', rows[5].children[0].textContent); assertEquals('0', rows[5].children[1].textContent); assertEquals(' foo', rows[6].children[0].textContent); assertEquals('false', rows[6].children[1].textContent); } function testAnalyzeSelectionWithSingleSliceCategory() { var selection = createSelectionWithSingleSlice(true); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(1, results.tables.length); var table = results.tables[0]; assertEquals('Selected slice:', table.tableHeader); assertEquals(4, table.rows.length); assertEquals('b', table.rows[0].text); assertEquals('foo', table.rows[1].text); assertEquals(0, table.rows[2].time); assertAlmostEquals(0.002, table.rows[3].time); } function testAnalyzeSelectionWithTwoSlices() { var selection = createSelectionWithTwoSlices(); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(1, results.tables.length); var table = results.tables[0]; assertEquals('Slices:', table.tableHeader); assertEquals(6, table.rows.length); assertEquals('a', table.rows[0].label); assertEquals(1, table.rows[0].occurences); assertAlmostEquals(0.04, table.rows[0].duration); assertEquals('aa', table.rows[1].label); assertEquals(1, table.rows[1].occurences); assertAlmostEquals(0.06, table.rows[1].duration); assertEquals('*Totals', table.rows[2].label); assertEquals(2, table.rows[2].occurences); assertAlmostEquals(0.1, table.rows[2].duration); assertEquals('Selection start', table.rows[4].label); assertAlmostEquals(0, table.rows[4].time); assertEquals('Selection extent', table.rows[5].label); assertAlmostEquals(0.18, table.rows[5].time); } function testAnalyzeSelectionWithTwoSlicesSameTitle() { var selection = createSelectionWithTwoSlicesSameTitle(); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(1, results.tables.length); var table = results.tables[0]; assertEquals('Slices:', table.tableHeader); assertEquals(5, table.rows.length); assertEquals('c', table.rows[0].label); assertEquals('2', table.rows[0].occurences); assertAlmostEquals(0.04, table.rows[0].details.min); assertAlmostEquals(0.05, table.rows[0].details.avg); assertAlmostEquals(0.06, table.rows[0].details.max); assertAlmostEquals(0.1, table.rows[0].duration); assertEquals('*Totals', table.rows[1].label); assertAlmostEquals(0.1, table.rows[1].duration); assertEquals('Selection start', table.rows[3].label); assertAlmostEquals(0, table.rows[3].time); assertEquals('Selection extent', table.rows[4].label); assertAlmostEquals(0.18, table.rows[4].time); } function testAnalyzeSelectionWithSingleCounter() { var selection = createSelectionWithCounters(1); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(1, results.tables.length); var table = results.tables[0]; assertEquals('Selected counter:', table.tableHeader); assertEquals(3, table.rows.length); assertEquals('Title', table.rows[0].label); assertEquals('Timestamp', table.rows[1].label); assertEquals('value', table.rows[2].label); assertEquals(10, table.rows[2].text); } function testAnalyzeSelectionWithBasicTwoSeriesTwoCounters() { var selection = createSelectionWithTwoSeriesTwoCounters(); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(1, results.tables.length); var table = results.tables[0]; assertEquals('Counters:', table.tableHeader); assertEquals(4, table.rows.length); assertEquals('ctr1: bytesallocated', table.rows[0].label); assertEquals('ctr1: bytesfree', table.rows[1].label); assertEquals('ctr2: bytesallocated', table.rows[2].label); assertEquals('ctr2: bytesfree', table.rows[3].label); } function testAnalyzeSelectionWithComplexSeriesTwoCounters() { var selection = createSelectionWithTwoCountersDiffSeriesDiffHits(); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(1, results.tables.length); var table = results.tables[0]; assertEquals('Counters:', table.tableHeader); assertEquals(3, table.rows.length); assertEquals('a: bytesallocated', table.rows[0].label); assertEquals('b: bytesallocated', table.rows[1].label); assertEquals('b: bytesfree', table.rows[2].label); } function testAnalyzeSelectionWithCounterAndSlices() { var selection = createSelectionWithSingleSliceSingleCounter(); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(2, results.tables.length); var sliceTable = results.tables[0]; var counterTable = results.tables[1]; assertEquals('Selected slice:', sliceTable.tableHeader); assertEquals(3, sliceTable.rows.length); assertEquals('Selected counter:', counterTable.tableHeader); assertEquals(4, counterTable.rows.length); } function testAnalyzeSelectionWithNormallyDistributedSamples() { var numSlices = 2; var selection = createSelectionWithNormallyDistributedSamples(numSlices); var results = new StubAnalysisResults(); tracing.analyzeSelection_(results, selection); assertEquals(1, results.tables.length); assertEquals('slice0', results.tables[0].rows[0].label); assertEquals( 63, Math.round(results.tables[0].rows[0].details.frequency)); assertEquals( 16, Math.round(results.tables[0].rows[0].details.frequency_stddev)); assertEquals('slice1', results.tables[0].rows[1].label); assertEquals( 63, Math.round(results.tables[0].rows[1].details.frequency)); assertEquals( 16, Math.round(results.tables[0].rows[1].details.frequency_stddev)); } </script> </body> </html>