forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Initial commit of a queueing delay metric:
- Adds new Queueing Delay metric to Smoothness - Moves BMF and SBMF TRACE_EVENT strings from thread_proxy.cc to benchmark_instrumentation.cc Review URL: https://codereview.chromium.org/301973011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@281647 0039d316-1c4b-4281-b951-d872f2087c98
- Loading branch information
ariblue@google.com
committed
Jul 8, 2014
1 parent
006e384
commit 1bbed7a
Showing
11 changed files
with
323 additions
and
18 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
tools/telemetry/telemetry/web_perf/metrics/rendering_frame.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
# Copyright 2014 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. | ||
|
||
from collections import defaultdict | ||
from telemetry.timeline import bounds | ||
from telemetry.timeline import slice as slice_module | ||
|
||
|
||
class MissingData(Exception): | ||
pass | ||
|
||
|
||
class NoBeginFrameIdException(Exception): | ||
pass | ||
|
||
|
||
class RenderingFrame(object): | ||
"""Object with information about the triggering of a BeginMainFrame event.""" | ||
send_begin_frame_event = 'ThreadProxy::ScheduledActionSendBeginMainFrame' | ||
begin_main_frame_event = 'ThreadProxy::BeginMainFrame' | ||
|
||
def __init__(self, events): | ||
all_send_begin_frame_events = [e for e in events | ||
if e.name == self.send_begin_frame_event] | ||
if len(all_send_begin_frame_events) != 1: | ||
raise MissingData('There must be at exactly one %s event.' % | ||
self.send_begin_frame_event) | ||
|
||
all_begin_main_frame_events = [e for e in events | ||
if e.name == self.begin_main_frame_event] | ||
if not all_begin_main_frame_events: | ||
raise MissingData('There must be at least one %s event.' % | ||
self.begin_main_frame_event) | ||
all_begin_main_frame_events.sort(key=lambda e: e.start) | ||
|
||
self._send_begin_frame = all_send_begin_frame_events[0] | ||
self._begin_main_frame = all_begin_main_frame_events[-1] | ||
|
||
self._bounds = bounds.Bounds() | ||
self._bounds.AddEvent(self._begin_main_frame) | ||
self._bounds.AddEvent(self._send_begin_frame) | ||
|
||
@staticmethod | ||
def IsEventUseful(event): | ||
return event.name in [RenderingFrame.send_begin_frame_event, | ||
RenderingFrame.begin_main_frame_event] | ||
|
||
@property | ||
def bounds(self): | ||
return self._bounds | ||
|
||
@property | ||
def queueing_duration(self): | ||
return self._begin_main_frame.start - self._send_begin_frame.start | ||
|
||
|
||
def GetFrameEventsInsideRange(renderer_process, timeline_range): | ||
"""Returns RenderingFrames for all relevant events in the timeline_range.""" | ||
# First filter all events from the renderer_process and turn them into a | ||
# dictonary of the form: | ||
# {0: [send_begin_frame, begin_main_frame, begin_main_frame], | ||
# 1: [begin_main_frame, send_begin_frame], | ||
# 2: [send_begin_frame, begin_main_frame]} | ||
begin_frame_events_by_id = defaultdict(list) | ||
for event in renderer_process.IterAllEvents( | ||
event_type_predicate=lambda t: t == slice_module.Slice, | ||
event_predicate=RenderingFrame.IsEventUseful): | ||
begin_frame_id = event.args.get('begin_frame_id', None) | ||
if begin_frame_id is None: | ||
raise NoBeginFrameIdException('Event is missing a begin_frame_id.') | ||
begin_frame_events_by_id[begin_frame_id].append(event) | ||
|
||
# Now, create RenderingFrames for events wherever possible. | ||
frames = [] | ||
for events in begin_frame_events_by_id.values(): | ||
try: | ||
frame = RenderingFrame(events) | ||
if frame.bounds.Intersects(timeline_range): | ||
frames.append(frame) | ||
except MissingData: | ||
continue | ||
frames.sort(key=lambda frame: frame.bounds.min) | ||
|
||
return frames |
162 changes: 162 additions & 0 deletions
162
tools/telemetry/telemetry/web_perf/metrics/rendering_frame_unittest.py
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
# Copyright 2014 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 unittest | ||
|
||
from telemetry.timeline import model | ||
import telemetry.timeline.bounds as timeline_bounds | ||
import telemetry.timeline.slice as tracing_slice | ||
from telemetry.web_perf.metrics.rendering_frame import GetFrameEventsInsideRange | ||
from telemetry.web_perf.metrics.rendering_frame import MissingData | ||
from telemetry.web_perf.metrics.rendering_frame import RenderingFrame | ||
|
||
|
||
class RenderingFrameTestData(object): | ||
|
||
def __init__(self): | ||
self._begin_frame_id = 0 | ||
self._events = [] | ||
self._renderer_process = model.TimelineModel().GetOrCreateProcess(pid=1) | ||
self._main_thread = self._renderer_process.GetOrCreateThread(tid=11) | ||
self._compositor_thread = self._renderer_process.GetOrCreateThread(tid=12) | ||
|
||
@property | ||
def events(self): | ||
return self._events | ||
|
||
@property | ||
def renderer_process(self): | ||
return self._renderer_process | ||
|
||
def AddSendEvent(self, ts=0, duration=1): | ||
self._begin_frame_id += 1 | ||
event = self._CreateEvent( | ||
RenderingFrame.send_begin_frame_event, ts, duration) | ||
self._compositor_thread.PushSlice(event) | ||
|
||
def AddBeginMainFrameEvent(self, ts=0, duration=1): | ||
event = self._CreateEvent( | ||
RenderingFrame.begin_main_frame_event, ts, duration) | ||
self._main_thread.PushSlice(event) | ||
|
||
def FinalizeImport(self): | ||
self._renderer_process.FinalizeImport() | ||
|
||
def _CreateEvent(self, event_name, ts, duration): | ||
event = tracing_slice.Slice(None, 'cc,benchmark', event_name, ts, | ||
duration=duration, args={'begin_frame_id': self._begin_frame_id}) | ||
self._events.append(event) | ||
return event | ||
|
||
|
||
def GenerateTimelineRange(start=0, end=100): | ||
timeline_range = timeline_bounds.Bounds() | ||
timeline_range.AddValue(start) | ||
timeline_range.AddValue(end) | ||
return timeline_range | ||
|
||
|
||
class RenderingFrameUnitTest(unittest.TestCase): | ||
|
||
def testRenderingFrame(self): | ||
d = RenderingFrameTestData() | ||
d.AddSendEvent(ts=10) | ||
d.AddBeginMainFrameEvent(ts=20) | ||
d.FinalizeImport() | ||
|
||
frame = RenderingFrame(d.events) | ||
self.assertEquals(10, frame.queueing_duration) | ||
|
||
def testRenderingFrameMissingSendBeginFrameEvents(self): | ||
d = RenderingFrameTestData() | ||
d.AddBeginMainFrameEvent(ts=10) | ||
d.FinalizeImport() | ||
|
||
self.assertRaises(MissingData, RenderingFrame, d.events) | ||
|
||
def testRenderingFrameDuplicateSendBeginFrameEvents(self): | ||
d = RenderingFrameTestData() | ||
d.AddSendEvent(ts=10) | ||
d.AddBeginMainFrameEvent(ts=20) | ||
d.AddSendEvent(ts=30) | ||
d.FinalizeImport() | ||
|
||
self.assertRaises(MissingData, RenderingFrame, d.events) | ||
|
||
def testRenderingFrameMissingBeginMainFrameEvents(self): | ||
d = RenderingFrameTestData() | ||
d.AddSendEvent(ts=10) | ||
d.FinalizeImport() | ||
|
||
self.assertRaises(MissingData, RenderingFrame, d.events) | ||
|
||
def testRenderingFrameDuplicateBeginMainFrameEvents(self): | ||
d = RenderingFrameTestData() | ||
d.AddSendEvent(ts=10) | ||
d.AddBeginMainFrameEvent(ts=20) | ||
d.AddBeginMainFrameEvent(ts=30) | ||
d.AddBeginMainFrameEvent(ts=40) | ||
d.FinalizeImport() | ||
|
||
frame = RenderingFrame(d.events) | ||
self.assertEquals(30, frame.queueing_duration) | ||
|
||
def testFrameEventMissingBeginFrameId(self): | ||
timeline = model.TimelineModel() | ||
process = timeline.GetOrCreateProcess(pid=1) | ||
main_thread = process.GetOrCreateThread(tid=11) | ||
timeline_range = timeline_bounds.Bounds() | ||
|
||
# Create an event without the begin_frame_id argument | ||
event = tracing_slice.Slice( | ||
None, 'cc,benchmark', RenderingFrame.begin_main_frame_event, 0) | ||
main_thread.PushSlice(event) | ||
process.FinalizeImport() | ||
self.assertRaises(Exception, GetFrameEventsInsideRange, process, | ||
timeline_range) | ||
|
||
def testGetFrameEventsInsideRange(self): | ||
"""Test a basic sequenece, with expected frame queueing delays A and B. | ||
|----A----| |--B--| | ||
Main: [1] [1] [2] | ||
Compositor: [1] [2] | ||
""" | ||
d = RenderingFrameTestData() | ||
d.AddSendEvent(ts=10) | ||
d.AddBeginMainFrameEvent(ts=20) | ||
d.AddBeginMainFrameEvent(ts=30) | ||
d.AddSendEvent(ts=40) | ||
d.AddBeginMainFrameEvent(ts=50) | ||
d.FinalizeImport() | ||
|
||
timeline_range = GenerateTimelineRange() | ||
frame_events = GetFrameEventsInsideRange(d.renderer_process, timeline_range) | ||
|
||
self.assertEquals(2, len(frame_events)) | ||
self.assertEquals(20, frame_events[0].queueing_duration) | ||
self.assertEquals(10, frame_events[1].queueing_duration) | ||
|
||
def testFrameEventsMissingDataNotIncluded(self): | ||
"""Test a sequenece missing an initial SendBeginFrame. | ||
Only one frame should be returned, with expected frame queueing delay A. | ||
|--A--| | ||
Main: [0] [0] [2] | ||
Compositor: [2] | ||
""" | ||
d = RenderingFrameTestData() | ||
d.AddBeginMainFrameEvent(ts=20) | ||
d.AddBeginMainFrameEvent(ts=30) | ||
d.AddSendEvent(ts=40) | ||
d.AddBeginMainFrameEvent(ts=50) | ||
d.FinalizeImport() | ||
|
||
timeline_range = GenerateTimelineRange() | ||
frame_events = GetFrameEventsInsideRange(d.renderer_process, timeline_range) | ||
|
||
self.assertEquals(1, len(frame_events)) | ||
self.assertEquals(10, frame_events[0].queueing_duration) |
Oops, something went wrong.