diff options
author | sullivan@chromium.org <sullivan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-09 17:51:59 +0000 |
---|---|---|
committer | sullivan@chromium.org <sullivan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-01-09 17:51:59 +0000 |
commit | e571b1d6610c5830341834563ba57aada8a52ef6 (patch) | |
tree | 68881722d3d9ebb88e8f1d25df937bfdd6f13a0f /tools | |
parent | 68b4ee2af94c17215aeb201f4088e6357598ea3d (diff) | |
download | chromium_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.py | 8 | ||||
-rw-r--r-- | tools/telemetry/telemetry/inspector_timeline.py | 110 | ||||
-rw-r--r-- | tools/telemetry/telemetry/inspector_timeline_unittest.py | 143 | ||||
-rw-r--r-- | tools/telemetry/telemetry/timeline_event.py | 48 | ||||
-rw-r--r-- | tools/telemetry/telemetry/timeline_event_unittest.py | 27 | ||||
-rw-r--r-- | tools/telemetry/telemetry/timeline_model.py | 23 | ||||
-rw-r--r-- | tools/telemetry/telemetry/timeline_model_unittest.py | 18 |
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'))) |