Commit 042de4d1 authored by sullivan@chromium.org's avatar sullivan@chromium.org

Revert 175782

> InspectorTimeline improvements
> 
> - Split out inspector-neutral parts of InspectorTimeline in preparation for
>   about:tracing-based timeline
> - Use event.name instead of type, which is in keeping with trace-viewer
> - Use ms on units since the rest of telemetry uses seconds, but
>   for precision reasons, ms makes sense to stick with
> - Move arguments to event.args so that params get consistent treatment
>   e.g. event.foo['bar'] is less consistent than event.args['foo']['bar']
> - Improved test coverage of model, events, parsing
> - event.self_time which represents
> - drop events lacking start- or end- times
> 
> R=bulach@chromium.org
> NOTRY=True
> 
> Review URL: https://chromiumcodereview.appspot.com/11818024

TBR=nduca@chromium.org
Review URL: https://codereview.chromium.org/11733008

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@175810 0039d316-1c4b-4281-b951-d872f2087c98
parent 515adc20
...@@ -15,7 +15,7 @@ class ImageDecoding(multi_page_benchmark.MultiPageBenchmark): ...@@ -15,7 +15,7 @@ class ImageDecoding(multi_page_benchmark.MultiPageBenchmark):
return tab.runtime.Evaluate('isDone') return tab.runtime.Evaluate('isDone')
decode_image_events = \ decode_image_events = \
tab.timeline.timeline_events.GetAllOfName('DecodeImage') tab.timeline.timeline_events.GetAllOfType('DecodeImage')
# If it is a real image benchmark, then store only the last-minIterations # If it is a real image benchmark, then store only the last-minIterations
# decode tasks. # decode tasks.
...@@ -26,9 +26,9 @@ class ImageDecoding(multi_page_benchmark.MultiPageBenchmark): ...@@ -26,9 +26,9 @@ class ImageDecoding(multi_page_benchmark.MultiPageBenchmark):
min_iterations = tab.runtime.Evaluate('minIterations') min_iterations = tab.runtime.Evaluate('minIterations')
decode_image_events = decode_image_events[-min_iterations:] decode_image_events = decode_image_events[-min_iterations:]
durations = [d.duration_ms for d in decode_image_events] elapsed_times = [d.elapsed_time for d in decode_image_events]
if not durations: if not elapsed_times:
results.Add('ImageDecoding_avg', 'ms', 'unsupported') results.Add('ImageDecoding_avg', 'ms', 'unsupported')
return return
image_decoding_avg = sum(durations) / len(durations) image_decoding_avg = sum(elapsed_times) / len(elapsed_times)
results.Add('ImageDecoding_avg', 'ms', image_decoding_avg) results.Add('ImageDecoding_avg', 'ms', image_decoding_avg)
# Copyright (c) 2012 The Chromium Authors. All rights reserved. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
from telemetry.timeline_event import TimelineEvent
from telemetry.timeline_model import TimelineModel
class InspectorBackendException(Exception): class InspectorBackendException(Exception):
pass pass
class TimelineEvent(object):
"""Represents a timeline event."""
def __init__(self, d):
self.__dict__.update(d)
@property
def type(self):
return self.__dict__.get('type')
@property
def start_time(self):
return self.__dict__.get('startTime', 0)
@property
def end_time(self):
return self.__dict__.get('endTime', 0)
@property
def elapsed_time(self):
return self.end_time - self.start_time
class TimelineEvents(object):
def __init__(self):
self._events = []
def AppendRawEvents(self, raw_inspector_stream):
if raw_inspector_stream.get('params', {}).get('record'):
self._FlattenEvents(raw_inspector_stream['params']['record'])
def _FlattenEvents(self, raw_inspector_events):
self._events.append(TimelineEvent(raw_inspector_events))
for child in raw_inspector_events.get('children', []):
self._FlattenEvents(child)
def GetAllOfType(self, type_name):
return [e for e in self._events if e.type == type_name]
class InspectorTimeline(object): class InspectorTimeline(object):
"""Implementation of dev tools timeline.""" """Implementation of dev tools timeline."""
class Recorder(object): class Recorder(object):
...@@ -24,17 +63,17 @@ class InspectorTimeline(object): ...@@ -24,17 +63,17 @@ class InspectorTimeline(object):
self._inspector_backend = inspector_backend self._inspector_backend = inspector_backend
self._tab = tab self._tab = tab
self._is_recording = False self._is_recording = False
self._timeline_model = None self._timeline_events = None
@property @property
def timeline_model(self): def timeline_events(self):
return self._timeline_model return self._timeline_events
def Start(self): def Start(self):
if self._is_recording: if self._is_recording:
return return
self._timeline_events = TimelineEvents()
self._is_recording = True self._is_recording = True
self._timeline_model = TimelineModel()
self._inspector_backend.RegisterDomain('Timeline', self._inspector_backend.RegisterDomain('Timeline',
self._OnNotification, self._OnClose) self._OnNotification, self._OnClose)
req = {'method': 'Timeline.start'} req = {'method': 'Timeline.start'}
...@@ -44,7 +83,6 @@ class InspectorTimeline(object): ...@@ -44,7 +83,6 @@ class InspectorTimeline(object):
if not self._is_recording: if not self._is_recording:
raise InspectorBackendException('Stop() called but not started') raise InspectorBackendException('Stop() called but not started')
self._is_recording = False self._is_recording = False
self._timeline_model.DidFinishRecording()
req = {'method': 'Timeline.stop'} req = {'method': 'Timeline.stop'}
self._SendSyncRequest(req) self._SendSyncRequest(req)
self._inspector_backend.UnregisterDomain('Timeline') self._inspector_backend.UnregisterDomain('Timeline')
...@@ -59,63 +97,7 @@ class InspectorTimeline(object): ...@@ -59,63 +97,7 @@ class InspectorTimeline(object):
if not self._is_recording: if not self._is_recording:
return return
if 'method' in msg and msg['method'] == 'Timeline.eventRecorded': if 'method' in msg and msg['method'] == 'Timeline.eventRecorded':
self._OnEventRecorded(msg) self._timeline_events.AppendRawEvents(msg)
def _OnEventRecorded(self, msg):
record = msg.get('params', {}).get('record')
if record:
newly_created_event = InspectorTimeline.RawEventToTimelineEvent(record)
self._timeline_model.AddEvent(newly_created_event)
@staticmethod
def RawEventToTimelineEvent(raw_inspector_event):
"""Converts raw_inspector_event to TimelineEvent."""
return InspectorTimeline._RawEventToTimelineEventRecursive(
None, raw_inspector_event)
@staticmethod
def _RawEventToTimelineEventRecursive(
parent_for_created_events, raw_inspector_event):
"""
Creates a new TimelineEvent for the raw_inspector_event, if possible, adding
it to the provided parent_for_created_events.
It then recurses on any child events found inside, building a tree of
TimelineEvents.
Returns the root of the created tree, or None.
"""
# Create a TimelineEvent for this raw_inspector_event if possible. Only
# events with start-time and end-time get imported.
if ('startTime' in raw_inspector_event and
'endTime' in raw_inspector_event):
args = {}
for x in raw_inspector_event:
if x in ('startTime', 'endTime', 'children'):
continue
args[x] = raw_inspector_event[x]
if len(args) == 0:
args = None
newly_created_event = TimelineEvent(
name=raw_inspector_event['type'],
start_time_ms=raw_inspector_event['startTime'],
duration_ms=(raw_inspector_event['endTime'] -
raw_inspector_event['startTime']),
args=args)
if parent_for_created_events:
parent_for_created_events.children.append(newly_created_event)
else:
newly_created_event = None
# Process any children events, creating TimelineEvents for them as well.
if newly_created_event:
parent_for_children = newly_created_event
else:
parent_for_children = parent_for_created_events
for child in raw_inspector_event.get('children', []):
InspectorTimeline._RawEventToTimelineEventRecursive(
parent_for_children, child)
return newly_created_event
def _OnClose(self): def _OnClose(self):
if self._is_recording: if self._is_recording:
......
# Copyright (c) 2012 The Chromium Authors. All rights reserved. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
import os import os
import unittest import unittest
from telemetry import inspector_timeline
from telemetry import tab_test_case from telemetry import tab_test_case
from telemetry import util from telemetry import util
from telemetry.inspector_timeline import InspectorTimeline
_SAMPLE_MESSAGE = {
'children': [
{'data': {},
'startTime': 1352783525921.823,
'type': 'BeginFrame',
'usedHeapSize': 1870736},
{'children': [],
'data': {'height': 723,
'width': 1272,
'x': 0,
'y': 0},
'endTime': 1352783525921.8992,
'frameId': '10.2',
'startTime': 1352783525921.8281,
'type': 'Layout',
'usedHeapSize': 1870736},
{'children': [
{'children': [],
'data': {'imageType': 'PNG'},
'endTime': 1352783525927.7939,
'startTime': 1352783525922.4241,
'type': 'DecodeImage',
'usedHeapSize': 1870736}
],
'data': {'height': 432,
'width': 1272,
'x': 0,
'y': 8},
'endTime': 1352783525927.9822,
'frameId': '10.2',
'startTime': 1352783525921.9292,
'type': 'Paint',
'usedHeapSize': 1870736}
],
'data': {},
'endTime': 1352783525928.041,
'startTime': 1352783525921.8049,
'type': 'Program'}
class InspectorEventParsingTest(unittest.TestCase):
def testParsingWithSampleData(self):
root_event = InspectorTimeline.RawEventToTimelineEvent(_SAMPLE_MESSAGE)
self.assertTrue(root_event)
decode_image_event = [
child for child in root_event.GetAllChildrenRecursive()
if child.name == 'DecodeImage'][0]
self.assertEquals(decode_image_event.args['data']['imageType'], 'PNG')
self.assertTrue(decode_image_event.duration_ms > 0)
def testParsingWithSimpleData(self):
raw_event = {'type': 'Foo',
'startTime': 1,
'endTime': 3,
'children': []}
event = InspectorTimeline.RawEventToTimelineEvent(raw_event)
self.assertEquals('Foo', event.name)
self.assertEquals(1, event.start_time_ms)
self.assertEquals(3, event.end_time_ms)
self.assertEquals(2, event.duration_ms)
self.assertEquals([], event.children)
def testParsingWithArgs(self): _SAMPLE_STREAM = [
raw_event = {'type': 'Foo', {u'method': u'Timeline.eventRecorded',
'startTime': 1, u'params': {u'record': {u'children': [
'endTime': 3, {u'data': {},
'foo': 7, u'startTime': 1352783525921.823,
'bar': {'x': 1}} u'type': u'BeginFrame',
event = InspectorTimeline.RawEventToTimelineEvent(raw_event) u'usedHeapSize': 1870736},
self.assertEquals('Foo', event.name) {u'children': [],
self.assertEquals(1, event.start_time_ms) u'data': {u'height': 723,
self.assertEquals(3, event.end_time_ms) u'width': 1272,
self.assertEquals(2, event.duration_ms) u'x': 0,
self.assertEquals([], event.children) u'y': 0},
self.assertEquals(7, event.args['foo']) u'endTime': 1352783525921.8992,
self.assertEquals(1, event.args['bar']['x']) u'frameId': u'10.2',
u'startTime': 1352783525921.8281,
u'type': u'Layout',
u'usedHeapSize': 1870736},
{u'children': [{u'children': [],
u'data': {u'imageType': u'PNG'},
u'endTime': 1352783525927.7939,
u'startTime': 1352783525922.4241,
u'type': u'DecodeImage',
u'usedHeapSize': 1870736}],
u'data': {u'height': 432,
u'width': 1272,
u'x': 0,
u'y': 8},
u'endTime': 1352783525927.9822,
u'frameId': u'10.2',
u'startTime': 1352783525921.9292,
u'type': u'Paint',
u'usedHeapSize': 1870736}],
u'data': {},
u'endTime': 1352783525928.041,
u'startTime': 1352783525921.8049,
u'type': u'Program'}}},
]
def testEventsWithNoStartTimeAreDropped(self):
raw_event = {'type': 'Foo',
'endTime': 1,
'children': []}
event = InspectorTimeline.RawEventToTimelineEvent(raw_event)
self.assertEquals(None, event)
def testEventsWithNoEndTimeAreDropped(self): class InspectorTimelineTest(unittest.TestCase):
raw_event = {'type': 'Foo', def testTimelineEventParsing(self):
'endTime': 1, timeline_events = inspector_timeline.TimelineEvents()
'children': []} for raw_events in _SAMPLE_STREAM:
event = InspectorTimeline.RawEventToTimelineEvent(raw_event) timeline_events.AppendRawEvents(raw_events)
self.assertEquals(None, event) decode_image_events = timeline_events.GetAllOfType('DecodeImage')
self.assertEquals(len(decode_image_events), 1)
self.assertEquals(decode_image_events[0].data['imageType'], 'PNG')
self.assertTrue(decode_image_events[0].elapsed_time > 0)
class InspectorTimelineTabTest(tab_test_case.TabTestCase): class InspectorTimelineTabTest(tab_test_case.TabTestCase):
...@@ -114,7 +75,7 @@ class InspectorTimelineTabTest(tab_test_case.TabTestCase): ...@@ -114,7 +75,7 @@ class InspectorTimelineTabTest(tab_test_case.TabTestCase):
def testGotTimeline(self): def testGotTimeline(self):
self._StartServer() self._StartServer()
image_url = self._browser.http_server.UrlOf('image.png') image_url = self._browser.http_server.UrlOf('image.png')
with InspectorTimeline.Recorder(self._tab.timeline): with inspector_timeline.InspectorTimeline.Recorder(self._tab.timeline):
self._tab.runtime.Execute( self._tab.runtime.Execute(
""" """
var done = false; var done = false;
...@@ -125,7 +86,7 @@ window.webkitRequestAnimationFrame(function() { done = true; }); ...@@ -125,7 +86,7 @@ window.webkitRequestAnimationFrame(function() { done = true; });
""" % image_url) """ % image_url)
self._WaitForAnimationFrame() self._WaitForAnimationFrame()
r = self._tab.timeline.timeline_model.GetAllOfName('DecodeImage') r = self._tab.timeline.timeline_events.GetAllOfType('DecodeImage')
self.assertTrue(len(r) > 0) self.assertTrue(len(r) > 0)
self.assertEquals(r[0].args['data']['imageType'], 'PNG') self.assertEquals(r[0].data['imageType'], 'PNG')
self.assertTrue(r[0].duration_ms > 0) self.assertTrue(r[0].elapsed_time > 0)
# Copyright (c) 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.
class TimelineEvent(object):
"""Represents a timeline event."""
def __init__(self, name, start_time_ms, duration_ms, args=None):
self.name = name
self.start_time_ms = start_time_ms
self.duration_ms = duration_ms
self.children = []
self.args = args
@property
def end_time_ms(self):
return self.start_time_ms + self.duration_ms
@property
def self_time_ms(self):
"""Time spent in this function less any time spent in child events."""
child_total = sum(
[e.duration_ms for e in self.children])
return self.duration_ms - child_total
def __repr__(self):
if self.args:
args_str = ', ' + repr(self.args)
else:
args_str = ''
return "TimelineEvent(name='%s', start_ms=%f, duration_ms=%s%s)" % (
self.name,
self.start_time_ms,
self.duration_ms,
args_str)
@staticmethod
def _GetAllChildrenRecursive(events, item):
events.append(item)
for child in item.children:
TimelineEvent._GetAllChildrenRecursive(events, child)
def GetAllChildrenRecursive(self, include_self=False):
events = []
TimelineEvent._GetAllChildrenRecursive(events, self)
if not include_self:
del events[0]
return events
# Copyright (c) 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 unittest
from telemetry.timeline_event import TimelineEvent
class TimelineEventTest(unittest.TestCase):
def testChildrenLogic(self):
# [ top ]
# [ a ] [ b ]
# [x]
top = TimelineEvent('top', 0, 10)
a = TimelineEvent('a', 1, 2)
x = TimelineEvent('x', 1.5, 0.25)
b = TimelineEvent('b', 5, 2)
top.children.extend([a, b])
a.children.append(x)
all_children = top.GetAllChildrenRecursive(include_self=True)
self.assertEquals([top, a, x, b], all_children)
self.assertEquals(x.self_time_ms, 0.25)
self.assertEquals(a.self_time_ms, 1.75) # 2 - 0.25
self.assertEquals(top.self_time_ms, 6) # 10 - 2 -2
# Copyright (c) 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.
class TimelineModel(object):
def __init__(self):
self._events = []
self._frozen = False
def AddEvent(self, event):
if self._frozen:
raise Exception("Cannot add events once recording is done")
self._events.extend(
event.GetAllChildrenRecursive(include_self=True))
def DidFinishRecording(self):
self._frozen = True
def GetAllEvents(self):
return self._events
def GetAllOfName(self, name):
return [e for e in self._events if e.name == name]
# Copyright (c) 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 unittest
from telemetry.timeline_event import TimelineEvent
from telemetry.timeline_model import TimelineModel
class TimelineModelUnittest(unittest.TestCase):
def testTimelineEventsOfType(self):
timeline_model = TimelineModel()
a = TimelineEvent('a', 0, 10)
b = TimelineEvent('b', 11, 10)
timeline_model.AddEvent(a)
timeline_model.AddEvent(b)
timeline_model.DidFinishRecording()
self.assertEquals(1, len(timeline_model.GetAllOfName('a')))
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment