summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorsullivan@chromium.org <sullivan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-09 17:51:59 +0000
committersullivan@chromium.org <sullivan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-01-09 17:51:59 +0000
commite571b1d6610c5830341834563ba57aada8a52ef6 (patch)
tree68881722d3d9ebb88e8f1d25df937bfdd6f13a0f /tools
parent68b4ee2af94c17215aeb201f4088e6357598ea3d (diff)
downloadchromium_src-e571b1d6610c5830341834563ba57aada8a52ef6.zip
chromium_src-e571b1d6610c5830341834563ba57aada8a52ef6.tar.gz
chromium_src-e571b1d6610c5830341834563ba57aada8a52ef6.tar.bz2
Revert 175810 - looks like this revert broke some tests.
> 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 TBR=sullivan@chromium.org Review URL: https://codereview.chromium.org/11817019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@175825 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'tools')
-rw-r--r--tools/perf/perf_tools/image_decoding_benchmark.py8
-rw-r--r--tools/telemetry/telemetry/inspector_timeline.py110
-rw-r--r--tools/telemetry/telemetry/inspector_timeline_unittest.py143
-rw-r--r--tools/telemetry/telemetry/timeline_event.py48
-rw-r--r--tools/telemetry/telemetry/timeline_event_unittest.py27
-rw-r--r--tools/telemetry/telemetry/timeline_model.py23
-rw-r--r--tools/telemetry/telemetry/timeline_model_unittest.py18
7 files changed, 275 insertions, 102 deletions
diff --git a/tools/perf/perf_tools/image_decoding_benchmark.py b/tools/perf/perf_tools/image_decoding_benchmark.py
index c0df84e..00d0d40 100644
--- a/tools/perf/perf_tools/image_decoding_benchmark.py
+++ b/tools/perf/perf_tools/image_decoding_benchmark.py
@@ -15,7 +15,7 @@ class ImageDecoding(multi_page_benchmark.MultiPageBenchmark):
return tab.runtime.Evaluate('isDone')
decode_image_events = \
- tab.timeline.timeline_events.GetAllOfType('DecodeImage')
+ tab.timeline.timeline_events.GetAllOfName('DecodeImage')
# If it is a real image benchmark, then store only the last-minIterations
# decode tasks.
@@ -26,9 +26,9 @@ class ImageDecoding(multi_page_benchmark.MultiPageBenchmark):
min_iterations = tab.runtime.Evaluate('minIterations')
decode_image_events = decode_image_events[-min_iterations:]
- elapsed_times = [d.elapsed_time for d in decode_image_events]
- if not elapsed_times:
+ durations = [d.duration_ms for d in decode_image_events]
+ if not durations:
results.Add('ImageDecoding_avg', 'ms', 'unsupported')
return
- image_decoding_avg = sum(elapsed_times) / len(elapsed_times)
+ image_decoding_avg = sum(durations) / len(durations)
results.Add('ImageDecoding_avg', 'ms', image_decoding_avg)
diff --git a/tools/telemetry/telemetry/inspector_timeline.py b/tools/telemetry/telemetry/inspector_timeline.py
index e0810c6..8faf38d 100644
--- a/tools/telemetry/telemetry/inspector_timeline.py
+++ b/tools/telemetry/telemetry/inspector_timeline.py
@@ -1,51 +1,12 @@
# 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.
-
+from telemetry.timeline_event import TimelineEvent
+from telemetry.timeline_model import TimelineModel
class InspectorBackendException(Exception):
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):
"""Implementation of dev tools timeline."""
class Recorder(object):
@@ -63,17 +24,17 @@ class InspectorTimeline(object):
self._inspector_backend = inspector_backend
self._tab = tab
self._is_recording = False
- self._timeline_events = None
+ self._timeline_model = None
@property
- def timeline_events(self):
- return self._timeline_events
+ def timeline_model(self):
+ return self._timeline_model
def Start(self):
if self._is_recording:
return
- self._timeline_events = TimelineEvents()
self._is_recording = True
+ self._timeline_model = TimelineModel()
self._inspector_backend.RegisterDomain('Timeline',
self._OnNotification, self._OnClose)
req = {'method': 'Timeline.start'}
@@ -83,6 +44,7 @@ class InspectorTimeline(object):
if not self._is_recording:
raise InspectorBackendException('Stop() called but not started')
self._is_recording = False
+ self._timeline_model.DidFinishRecording()
req = {'method': 'Timeline.stop'}
self._SendSyncRequest(req)
self._inspector_backend.UnregisterDomain('Timeline')
@@ -97,7 +59,63 @@ class InspectorTimeline(object):
if not self._is_recording:
return
if 'method' in msg and msg['method'] == 'Timeline.eventRecorded':
- self._timeline_events.AppendRawEvents(msg)
+ self._OnEventRecorded(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):
if self._is_recording:
diff --git a/tools/telemetry/telemetry/inspector_timeline_unittest.py b/tools/telemetry/telemetry/inspector_timeline_unittest.py
index 9e0ac7b..6a7af8d 100644
--- a/tools/telemetry/telemetry/inspector_timeline_unittest.py
+++ b/tools/telemetry/telemetry/inspector_timeline_unittest.py
@@ -1,63 +1,102 @@
# 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.
-
import os
import unittest
-from telemetry import inspector_timeline
from telemetry import tab_test_case
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)
-_SAMPLE_STREAM = [
-{u'method': u'Timeline.eventRecorded',
- u'params': {u'record': {u'children': [
- {u'data': {},
- u'startTime': 1352783525921.823,
- u'type': u'BeginFrame',
- u'usedHeapSize': 1870736},
- {u'children': [],
- u'data': {u'height': 723,
- u'width': 1272,
- u'x': 0,
- u'y': 0},
- u'endTime': 1352783525921.8992,
- 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 testParsingWithArgs(self):
+ raw_event = {'type': 'Foo',
+ 'startTime': 1,
+ 'endTime': 3,
+ 'foo': 7,
+ 'bar': {'x': 1}}
+ 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)
+ self.assertEquals(7, event.args['foo'])
+ self.assertEquals(1, event.args['bar']['x'])
+ def testEventsWithNoStartTimeAreDropped(self):
+ raw_event = {'type': 'Foo',
+ 'endTime': 1,
+ 'children': []}
+ event = InspectorTimeline.RawEventToTimelineEvent(raw_event)
+ self.assertEquals(None, event)
-class InspectorTimelineTest(unittest.TestCase):
- def testTimelineEventParsing(self):
- timeline_events = inspector_timeline.TimelineEvents()
- for raw_events in _SAMPLE_STREAM:
- timeline_events.AppendRawEvents(raw_events)
- 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)
+ def testEventsWithNoEndTimeAreDropped(self):
+ raw_event = {'type': 'Foo',
+ 'endTime': 1,
+ 'children': []}
+ event = InspectorTimeline.RawEventToTimelineEvent(raw_event)
+ self.assertEquals(None, event)
class InspectorTimelineTabTest(tab_test_case.TabTestCase):
@@ -75,7 +114,7 @@ class InspectorTimelineTabTest(tab_test_case.TabTestCase):
def testGotTimeline(self):
self._StartServer()
image_url = self._browser.http_server.UrlOf('image.png')
- with inspector_timeline.InspectorTimeline.Recorder(self._tab.timeline):
+ with InspectorTimeline.Recorder(self._tab.timeline):
self._tab.runtime.Execute(
"""
var done = false;
@@ -86,7 +125,7 @@ window.webkitRequestAnimationFrame(function() { done = true; });
""" % image_url)
self._WaitForAnimationFrame()
- r = self._tab.timeline.timeline_events.GetAllOfType('DecodeImage')
+ r = self._tab.timeline.timeline_model.GetAllOfName('DecodeImage')
self.assertTrue(len(r) > 0)
- self.assertEquals(r[0].data['imageType'], 'PNG')
- self.assertTrue(r[0].elapsed_time > 0)
+ self.assertEquals(r[0].args['data']['imageType'], 'PNG')
+ self.assertTrue(r[0].duration_ms > 0)
diff --git a/tools/telemetry/telemetry/timeline_event.py b/tools/telemetry/telemetry/timeline_event.py
new file mode 100644
index 0000000..bbcafb2
--- /dev/null
+++ b/tools/telemetry/telemetry/timeline_event.py
@@ -0,0 +1,48 @@
+# 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
diff --git a/tools/telemetry/telemetry/timeline_event_unittest.py b/tools/telemetry/telemetry/timeline_event_unittest.py
new file mode 100644
index 0000000..94d59c1
--- /dev/null
+++ b/tools/telemetry/telemetry/timeline_event_unittest.py
@@ -0,0 +1,27 @@
+# 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
+
diff --git a/tools/telemetry/telemetry/timeline_model.py b/tools/telemetry/telemetry/timeline_model.py
new file mode 100644
index 0000000..fe19a91
--- /dev/null
+++ b/tools/telemetry/telemetry/timeline_model.py
@@ -0,0 +1,23 @@
+# 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]
diff --git a/tools/telemetry/telemetry/timeline_model_unittest.py b/tools/telemetry/telemetry/timeline_model_unittest.py
new file mode 100644
index 0000000..1adf9d9
--- /dev/null
+++ b/tools/telemetry/telemetry/timeline_model_unittest.py
@@ -0,0 +1,18 @@
+# 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')))