diff options
author | ernstm@chromium.org <ernstm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-07 06:49:49 +0000 |
---|---|---|
committer | ernstm@chromium.org <ernstm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-11-07 06:49:49 +0000 |
commit | 8d7dd7ade794c71be49c7a954c4d4b4ca1ab6c01 (patch) | |
tree | e33d98812aabbebdf93f5ee21b1672f190602d43 | |
parent | e74c9bc2d1ebbe10abd964e31454b59db5e8c0bc (diff) | |
download | chromium_src-8d7dd7ade794c71be49c7a954c4d4b4ca1ab6c01.zip chromium_src-8d7dd7ade794c71be49c7a954c4d4b4ca1ab6c01.tar.gz chromium_src-8d7dd7ade794c71be49c7a954c4d4b4ca1ab6c01.tar.bz2 |
telemetry: Add RenderingStatsUnitTest.
SmoothnessMetricUnitTest mixes testing of TimelineModel,
SmoothnessMetric, and RenderingStats. This patch adds a dedicated unit test
for RenderingStats only. It creates a TimelineModel directly, without using
TraceEventTimelineImporter (which has its own unit test). RenderingStatsUnitTest
verifies that RenderingStats extracts the correct events from the model.
This patch also fixes a bug in TimelineModel.FindTimelineMarkers uncovered
by the new test.
SmoothnessMetricUnitTest itself will be refactored in a separate CL.
R=nduca@chromium.org, tonyg@chromium.org
BUG=314877
Review URL: https://codereview.chromium.org/59403008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233512 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | tools/perf/metrics/rendering_stats.py | 5 | ||||
-rw-r--r-- | tools/perf/metrics/rendering_stats_unittest.py | 192 | ||||
-rw-r--r-- | tools/telemetry/telemetry/core/timeline/model.py | 27 |
3 files changed, 207 insertions, 17 deletions
diff --git a/tools/perf/metrics/rendering_stats.py b/tools/perf/metrics/rendering_stats.py index 024802c..1114a2f 100644 --- a/tools/perf/metrics/rendering_stats.py +++ b/tools/perf/metrics/rendering_stats.py @@ -20,10 +20,7 @@ class RenderingStats(object): """ assert(len(timeline_markers) > 0) self.renderer_process = renderer_process - self.start = timeline_markers[0].start - self.end = timeline_markers[-1].start + timeline_markers[-1].duration - self.frame_count = [] self.frame_timestamps = [] self.frame_times = [] self.paint_time = [] @@ -62,7 +59,6 @@ class RenderingStats(object): frame_count = event.args['data']['frame_count'] else: frame_count = event.args['data']['screen_frame_count'] - self.frame_count.append(frame_count) if frame_count > 1: raise ValueError, 'trace contains multi-frame render stats' if frame_count == 1: @@ -104,7 +100,6 @@ class RenderingStats(object): frame_count = event.args['data']['frame_count'] else: frame_count = event.args['data']['screen_frame_count'] - self.frame_count.append(frame_count) if frame_count > 1: raise ValueError, 'trace contains multi-frame render stats' if frame_count == 1: diff --git a/tools/perf/metrics/rendering_stats_unittest.py b/tools/perf/metrics/rendering_stats_unittest.py new file mode 100644 index 0000000..ec18597 --- /dev/null +++ b/tools/perf/metrics/rendering_stats_unittest.py @@ -0,0 +1,192 @@ +# Copyright 2013 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. + +import random +import unittest + +from metrics.rendering_stats import RenderingStats +from telemetry.core.timeline import model + + +class MockTimer(object): + """A mock timer class which can generate random durations. + + An instance of this class is used as a global timer to generate random + durations for stats and consistent timestamps for all mock trace events. + The unit of time is milliseconds. + """ + def __init__(self): + self.milliseconds = 0 + + def Get(self): + return self.milliseconds + + def Advance(self, low=0, high=1): + delta = random.uniform(low, high) + self.milliseconds += delta + return delta + + +class ReferenceRenderingStats(object): + """ Stores expected data for comparison with actual RenderingStats """ + def __init__(self): + self.frame_timestamps = [] + self.frame_times = [] + self.paint_time = [] + self.painted_pixel_count = [] + self.record_time = [] + self.recorded_pixel_count = [] + self.rasterize_time = [] + self.rasterized_pixel_count = [] + + +def AddMainThreadRenderingStats(mock_timer, thread, first_frame, + ref_stats = None): + """ Adds a random main thread rendering stats event. + + thread: The timeline model thread to which the event will be added. + first_frame: Is this the first frame within the bounds of an action? + ref_stats: A ReferenceRenderingStats object to record expected values. + """ + # Create randonm data and timestap for main thread rendering stats. + data = { 'frame_count': 0, + 'paint_time': 0.0, + 'painted_pixel_count': 0, + 'record_time': mock_timer.Advance(2, 4) / 1000.0, + 'recorded_pixel_count': 3000*3000 } + timestamp = mock_timer.Get() + + # Add a slice with the event data to the given thread. + thread.PushCompleteSlice( + 'benchmark', 'BenchmarkInstrumentation::MainThreadRenderingStats', + timestamp, 0.0, {'data': data}) + + if not ref_stats: + return + + # Add timestamp only if a frame was output + if data['frame_count'] == 1: + if not first_frame: + # Add frame_time if this is not the first frame in within the bounds of an + # action. + prev_timestamp = ref_stats.frame_timestamps[-1] + ref_stats.frame_times.append(round(timestamp - prev_timestamp, 2)) + ref_stats.frame_timestamps.append(timestamp) + + ref_stats.paint_time.append(data['paint_time'] * 1000.0) + ref_stats.painted_pixel_count.append(data['painted_pixel_count']) + ref_stats.record_time.append(data['record_time'] * 1000.0) + ref_stats.recorded_pixel_count.append(data['recorded_pixel_count']) + + +def AddImplThreadRenderingStats(mock_timer, thread, first_frame, + ref_stats = None): + """ Adds a random impl thread rendering stats event. + + thread: The timeline model thread to which the event will be added. + first_frame: Is this the first frame within the bounds of an action? + ref_stats: A ReferenceRenderingStats object to record expected values. + """ + # Create randonm data and timestap for impl thread rendering stats. + data = { 'frame_count': 1, + 'rasterize_time': mock_timer.Advance(5, 10) / 1000.0, + 'rasterized_pixel_count': 1280*720 } + timestamp = mock_timer.Get() + + # Add a slice with the event data to the given thread. + thread.PushCompleteSlice( + 'benchmark', 'BenchmarkInstrumentation::ImplThreadRenderingStats', + timestamp, 0.0, {'data': data}) + + if not ref_stats: + return + + # Add timestamp only if a frame was output + if data['frame_count'] == 1: + if not first_frame: + # Add frame_time if this is not the first frame in within the bounds of an + # action. + prev_timestamp = ref_stats.frame_timestamps[-1] + ref_stats.frame_times.append(round(timestamp - prev_timestamp, 2)) + ref_stats.frame_timestamps.append(timestamp) + + ref_stats.rasterize_time.append(data['rasterize_time'] * 1000.0) + ref_stats.rasterized_pixel_count.append(data['rasterized_pixel_count']) + + +class RenderingStatsUnitTest(unittest.TestCase): + def testFromTimeline(self): + timeline = model.TimelineModel() + + # Create a browser process and a renderer process, and a main thread and + # impl thread for each. + browser = timeline.GetOrCreateProcess(pid = 1) + browser_main = browser.GetOrCreateThread(tid = 11) + browser_compositor = browser.GetOrCreateThread(tid = 12) + renderer = timeline.GetOrCreateProcess(pid = 2) + renderer_main = renderer.GetOrCreateThread(tid = 21) + renderer_compositor = renderer.GetOrCreateThread(tid = 22) + + timer = MockTimer() + ref_stats = ReferenceRenderingStats() + + # Create 10 main and impl rendering stats events for Action A. + timer.Advance() + renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '') + for i in xrange(0, 10): + first = (i == 0) + AddMainThreadRenderingStats(timer, renderer_main, first, ref_stats) + AddImplThreadRenderingStats(timer, renderer_compositor, first, ref_stats) + AddMainThreadRenderingStats(timer, browser_main, first, None) + AddImplThreadRenderingStats(timer, browser_compositor, first, None) + renderer_main.EndSlice(timer.Get()) + + # Create 5 main and impl rendering stats events not within any action. + for i in xrange(0, 5): + first = (i == 0) + AddMainThreadRenderingStats(timer, renderer_main, first, None) + AddImplThreadRenderingStats(timer, renderer_compositor, first, None) + AddMainThreadRenderingStats(timer, browser_main, first, None) + AddImplThreadRenderingStats(timer, browser_compositor, first, None) + + # Create 10 main and impl rendering stats events for Action B. + timer.Advance() + renderer_main.BeginSlice('webkit.console', 'ActionB', timer.Get(), '') + for i in xrange(0, 10): + first = (i == 0) + AddMainThreadRenderingStats(timer, renderer_main, first, ref_stats) + AddImplThreadRenderingStats(timer, renderer_compositor, first, ref_stats) + AddMainThreadRenderingStats(timer, browser_main, first, None) + AddImplThreadRenderingStats(timer, browser_compositor, first, None) + renderer_main.EndSlice(timer.Get()) + + # Create 10 main and impl rendering stats events for Action A. + timer.Advance() + renderer_main.BeginSlice('webkit.console', 'ActionA', timer.Get(), '') + for i in xrange(0, 10): + first = (i == 0) + AddMainThreadRenderingStats(timer, renderer_main, first, ref_stats) + AddImplThreadRenderingStats(timer, renderer_compositor, first, ref_stats) + AddMainThreadRenderingStats(timer, browser_main, first, None) + AddImplThreadRenderingStats(timer, browser_compositor, first, None) + renderer_main.EndSlice(timer.Get()) + + renderer_main.FinalizeImport() + renderer_compositor.FinalizeImport() + + timeline_markers = timeline.FindTimelineMarkers( + ['ActionA', 'ActionB', 'ActionA']) + stats = RenderingStats(renderer, timeline_markers) + + # Compare rendering stats to reference. + self.assertEquals(stats.frame_timestamps, ref_stats.frame_timestamps) + self.assertEquals(stats.frame_times, ref_stats.frame_times) + self.assertEquals(stats.rasterize_time, ref_stats.rasterize_time) + self.assertEquals(stats.rasterized_pixel_count, + ref_stats.rasterized_pixel_count) + self.assertEquals(stats.paint_time, ref_stats.paint_time) + self.assertEquals(stats.painted_pixel_count, ref_stats.painted_pixel_count) + self.assertEquals(stats.record_time, ref_stats.record_time) + self.assertEquals(stats.recorded_pixel_count, + ref_stats.recorded_pixel_count) diff --git a/tools/telemetry/telemetry/core/timeline/model.py b/tools/telemetry/telemetry/core/timeline/model.py index 61ab315..f31d44a 100644 --- a/tools/telemetry/telemetry/core/timeline/model.py +++ b/tools/telemetry/telemetry/core/timeline/model.py @@ -139,30 +139,33 @@ class TimelineModel(object): self._processes[pid] = tracing_process.Process(self, pid) return self._processes[pid] - def FindTimelineMarkers(self, timeline_marker_labels): + def FindTimelineMarkers(self, timeline_marker_names): """Find the timeline events with the given names. - If the number and order of events found does not match the labels, + If the number and order of events found does not match the names, raise an error. """ - # Make sure labels are in a list and remove all None labels - if not isinstance(timeline_marker_labels, list): - timeline_marker_labels = [timeline_marker_labels] - labels = [x for x in timeline_marker_labels if x is not None] + # Make sure names are in a list and remove all None names + if not isinstance(timeline_marker_names, list): + timeline_marker_names = [timeline_marker_names] + names = [x for x in timeline_marker_names if x is not None] - # Gather all events that match the labels and sort them. + # Gather all events that match the names and sort them. events = [] - for label in labels: - events.extend([s for s in self.GetAllEventsOfName(label) + name_set = set() + for name in names: + name_set.add(name) + for name in name_set: + events.extend([s for s in self.GetAllEventsOfName(name) if s.parent_slice == None]) events.sort(key=attrgetter('start')) - # Check if the number and order of events matches the provided labels, + # Check if the number and order of events matches the provided names, # and that the events don't overlap. - if len(events) != len(labels): + if len(events) != len(names): raise MarkerMismatchError() for (i, event) in enumerate(events): - if event.name != labels[i]: + if event.name != names[i]: raise MarkerMismatchError() for i in xrange(0, len(events)): for j in xrange(i+1, len(events)): |