summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorernstm@chromium.org <ernstm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-07 06:49:49 +0000
committerernstm@chromium.org <ernstm@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-11-07 06:49:49 +0000
commit8d7dd7ade794c71be49c7a954c4d4b4ca1ab6c01 (patch)
treee33d98812aabbebdf93f5ee21b1672f190602d43
parente74c9bc2d1ebbe10abd964e31454b59db5e8c0bc (diff)
downloadchromium_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.py5
-rw-r--r--tools/perf/metrics/rendering_stats_unittest.py192
-rw-r--r--tools/telemetry/telemetry/core/timeline/model.py27
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)):