Commit 8d0425c1 authored by ernstm@chromium.org's avatar ernstm@chromium.org

telemetry: Add GetRendererProcessFromTab to timeline model.

R=nduca@chromium.org
BUG=264308

Review URL: https://codereview.chromium.org/29423005

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@233417 0039d316-1c4b-4281-b951-d872f2087c98
parent b58419fd
......@@ -11,6 +11,8 @@ from telemetry.page import page_measurement
from telemetry.core.timeline.model import MarkerMismatchError
from telemetry.core.timeline.model import MarkerOverlapError
TIMELINE_MARKER = 'RasterizeAndRecord'
class RasterizeAndRecord(page_measurement.PageMeasurement):
def __init__(self):
......@@ -97,7 +99,7 @@ class RasterizeAndRecord(page_measurement.PageMeasurement):
'window.__rafFired = false;'
'window.webkitRequestAnimationFrame(function() {'
'chrome.gpuBenchmarking.setNeedsDisplayOnAllLayers();'
'console.time("' + rendering_stats.RENDER_PROCESS_MARKER + '");'
'console.time("' + TIMELINE_MARKER + '");'
'window.__rafFired = true;'
'});')
# Wait until the frame was drawn.
......@@ -106,15 +108,15 @@ class RasterizeAndRecord(page_measurement.PageMeasurement):
# TODO(ernstm): replace by call-back.
time.sleep(float(self.options.stop_wait_time))
tab.ExecuteJavaScript(
'console.timeEnd("' + rendering_stats.RENDER_PROCESS_MARKER + '")')
'console.timeEnd("' + TIMELINE_MARKER + '")')
timeline = tab.browser.StopTracing().AsTimelineModel()
try:
timeline_markers = timeline.FindTimelineMarkers(
rendering_stats.RENDER_PROCESS_MARKER)
timeline_markers = timeline.FindTimelineMarkers(TIMELINE_MARKER)
except (MarkerMismatchError, MarkerOverlapError) as e:
raise page_measurement.MeasurementFailure(str(e))
stats = rendering_stats.RenderingStats(timeline_markers, timeline_markers)
renderer_process = timeline.GetRendererProcessFromTab(tab)
stats = rendering_stats.RenderingStats(renderer_process, timeline_markers)
results.Add('rasterize_time', 'ms',
max(stats.rasterize_time))
......
......@@ -4,11 +4,9 @@
from operator import attrgetter
RENDER_PROCESS_MARKER = 'RenderProcessMarker'
class RenderingStats(object):
def __init__(self, render_process_marker, timeline_markers):
def __init__(self, renderer_process, timeline_markers):
"""
Utility class for extracting rendering statistics from the timeline (or
other loggin facilities), and providing them in a common format to classes
......@@ -20,9 +18,8 @@ class RenderingStats(object):
All *_time values are measured in milliseconds.
"""
assert(len(render_process_marker) == 1)
assert(len(timeline_markers) > 0)
self.renderer_process = render_process_marker[0].start_thread.parent
self.renderer_process = renderer_process
self.start = timeline_markers[0].start
self.end = timeline_markers[-1].start + timeline_markers[-1].duration
......
......@@ -9,7 +9,7 @@ from telemetry.core.timeline.model import MarkerMismatchError
from telemetry.core.timeline.model import MarkerOverlapError
from telemetry.page import page_measurement
TIMELINE_MARKER = 'SmoothnessMetric'
TIMELINE_MARKER = 'Smoothness'
class NotEnoughFramesError(page_measurement.MeasurementFailure):
......@@ -24,16 +24,16 @@ class NoSupportedActionError(page_measurement.MeasurementFailure):
'None of the actions is supported by smoothness measurement')
def GetTimelineMarkerLabelsFromAction(compound_action):
timeline_marker_labels = []
def GetTimelineMarkerNamesFromAction(compound_action):
timeline_marker_names = []
if not isinstance(compound_action, list):
compound_action = [compound_action]
for action in compound_action:
if action.GetTimelineMarkerLabel():
timeline_marker_labels.append(action.GetTimelineMarkerLabel())
if not timeline_marker_labels:
if action.GetTimelineMarkerName():
timeline_marker_names.append(action.GetTimelineMarkerName())
if not timeline_marker_names:
raise NoSupportedActionError()
return timeline_marker_labels
return timeline_marker_names
class SmoothnessMetric(Metric):
......@@ -51,25 +51,31 @@ class SmoothnessMetric(Metric):
def Stop(self, page, tab):
tab.ExecuteJavaScript('console.timeEnd("' + TIMELINE_MARKER + '")')
timeline_model = tab.browser.StopTracing().AsTimelineModel()
render_process_marker = timeline_model.FindTimelineMarkers(TIMELINE_MARKER)
timeline_marker_labels = GetTimelineMarkerLabelsFromAction(
smoothness_marker = timeline_model.FindTimelineMarkers(TIMELINE_MARKER)
timeline_marker_names = GetTimelineMarkerNamesFromAction(
self._compound_action)
try:
timeline_markers = timeline_model.FindTimelineMarkers(
timeline_marker_labels)
timeline_marker_names)
except MarkerMismatchError:
# TODO(ernstm): re-raise exception as MeasurementFailure when the
# reference build was updated.
timeline_markers = render_process_marker
timeline_markers = smoothness_marker
except MarkerOverlapError as e:
raise page_measurement.MeasurementFailure(str(e))
renderer_process = timeline_model.GetRendererProcessFromTab(tab)
self._stats = rendering_stats.RenderingStats(
render_process_marker, timeline_markers)
renderer_process, timeline_markers)
if not self._stats.frame_times:
raise NotEnoughFramesError()
def SetStats(self, stats):
""" Pass in a RenderingStats object directly. For unittests that don't call
Start/Stop.
"""
self._stats = stats
def AddResults(self, tab, results):
# List of raw frame times.
......
......@@ -8,12 +8,13 @@ import unittest
from metrics import smoothness
from metrics import statistics
from metrics import rendering_stats
from telemetry.core.backends.chrome.tracing_backend import RawTraceResultImpl
from telemetry.core.backends.chrome.trace_result import TraceResult
from telemetry.core.trace_result import TraceResult
from telemetry.core.backends.chrome.tracing_backend import ChromeRawTraceResult
from telemetry.page import page
from telemetry.page.page_measurement_results import PageMeasurementResults
SYNTHETIC_GESTURE_MARKER = 'SyntheticGestureController::running'
RENDERER_PROCESS_MARKER = 'RendererProcessMarker'
class MockTimer(object):
......@@ -106,7 +107,7 @@ class SmoothnessMetricUnitTest(unittest.TestCase):
# Append start trace events for the timeline marker and gesture marker,
# with some amount of time in between them.
trace_events.append({'name': rendering_stats.RENDER_PROCESS_MARKER,
trace_events.append({'name': RENDERER_PROCESS_MARKER,
'tts': mock_timer.microseconds,
'args': {},
'pid': 20978,
......@@ -157,7 +158,7 @@ class SmoothnessMetricUnitTest(unittest.TestCase):
'ph': 'F', # Phase: finish.
'id': '0xabcde'})
mock_timer.Advance()
trace_events.append({'name': rendering_stats.RENDER_PROCESS_MARKER,
trace_events.append({'name': RENDERER_PROCESS_MARKER,
'tts': mock_timer.microseconds,
'args': {},
'pid': 20978,
......@@ -168,23 +169,24 @@ class SmoothnessMetricUnitTest(unittest.TestCase):
'id': '0x12345'})
# Create a timeline object from the trace.
trace_impl = RawTraceResultImpl(trace_events)
trace_result = TraceResult(trace_impl)
trace_result = TraceResult(ChromeRawTraceResult(trace_events))
timeline = trace_result.AsTimelineModel()
# Find the timeline marker and gesture marker in the timeline,
# and create a RenderingStats object.
render_process_marker = timeline.FindTimelineMarkers(
rendering_stats.RENDER_PROCESS_MARKER)
renderer_process_markers = timeline.FindTimelineMarkers(
RENDERER_PROCESS_MARKER)
self.assertEquals(len(renderer_process_markers), 1)
renderer_process = renderer_process_markers[0].start_thread.parent
timeline_markers = timeline.FindTimelineMarkers(
SYNTHETIC_GESTURE_MARKER)
stats = rendering_stats.RenderingStats(
render_process_marker, timeline_markers)
stats = rendering_stats.RenderingStats(renderer_process, timeline_markers)
# Make a results object and add results to it from the smoothness metric.
results = PageMeasurementResults()
results.WillMeasurePage(page.Page('http://foo.com/', None))
smoothness_metric = smoothness.SmoothnessMetric(stats)
smoothness_metric = smoothness.SmoothnessMetric(None)
smoothness_metric.SetStats(stats)
smoothness_metric.AddResults(None, results)
results.DidMeasurePage()
......
......@@ -256,6 +256,12 @@ class ChromeBrowserBackend(browser_backend.BrowserBackend):
def StopTracing(self):
""" Stops tracing and returns the result as TraceResult object. """
for (i, debugger_url) in enumerate(self._browser.tabs):
tab = self.tab_list_backend.Get(i, None)
if tab:
tab.ExecuteJavaScript('console.time("' + debugger_url + '")')
tab.ExecuteJavaScript('console.timeEnd("' + debugger_url + '")')
self._tracing_backend.AddTabToMarkerMapping(tab, debugger_url)
return self._tracing_backend.StopTracing()
def GetProcessName(self, cmd_line):
......
# 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.
class ChromeTraceResult(object):
def __init__(self, tracing_data, tab_to_marker_mapping = None):
self._tracing_data = tracing_data
if tab_to_marker_mapping == None:
self._tab_to_marker_mapping = {}
else:
self._tab_to_marker_mapping = tab_to_marker_mapping
def Serialize(self, f):
"""Serializes the trace result to a file-like object"""
raise NotImplementedError()
def AsTimelineModel(self):
"""Parses the trace result into a timeline model for in-memory
manipulation."""
timeline = self._CreateTimelineModel()
for key, value in self._tab_to_marker_mapping.iteritems():
timeline_markers = timeline.FindTimelineMarkers(value)
assert(len(timeline_markers) == 1)
renderer_process = timeline_markers[0].start_thread.parent
timeline.AddCoreObjectToContainerMapping(key, renderer_process)
return timeline
def _CreateTimelineModel(self):
raise NotImplementedError()
......@@ -8,7 +8,8 @@ import logging
import socket
import threading
from telemetry.core.backends.chrome import trace_result
from telemetry.core import trace_result
from telemetry.core.backends.chrome import chrome_trace_result
from telemetry.core.backends.chrome import websocket
from telemetry.core.backends.chrome import websocket_browser_connection
from telemetry.core.timeline import model
......@@ -21,9 +22,10 @@ class TracingUnsupportedException(Exception):
# protocol, where trace data were sent as JSON-serialized strings. DevTools
# now send the data as raw objects within the protocol message JSON, so there's
# no need in extra de-serialization. We might want to remove this in the future.
class TraceResultImpl(object):
def __init__(self, tracing_data):
self._tracing_data = tracing_data
class ChromeLegacyTraceResult(chrome_trace_result.ChromeTraceResult):
def __init__(self, tracing_data, tab_to_marker_mapping = None):
super(ChromeLegacyTraceResult, self).__init__(
tracing_data, tab_to_marker_mapping)
def Serialize(self, f):
f.write('{"traceEvents": [')
......@@ -43,25 +45,26 @@ class TraceResultImpl(object):
f.write(d[i])
f.write(']}')
def AsTimelineModel(self):
def _CreateTimelineModel(self):
f = cStringIO.StringIO()
self.Serialize(f)
return model.TimelineModel(
event_data=f.getvalue(),
shift_world_to_zero=False)
# RawTraceResultImpl differs from TraceResultImpl above in that
# ChromeRawTraceResult differs from ChromeLegacyTraceResult above in that
# data are kept as a list of dicts, not strings.
class RawTraceResultImpl(object):
def __init__(self, tracing_data):
self._tracing_data = tracing_data
class ChromeRawTraceResult(chrome_trace_result.ChromeTraceResult):
def __init__(self, tracing_data, tab_to_marker_mapping = None):
super(ChromeRawTraceResult, self).__init__(
tracing_data, tab_to_marker_mapping)
def Serialize(self, f):
f.write('{"traceEvents":')
json.dump(self._tracing_data, f)
f.write('}')
def AsTimelineModel(self):
def _CreateTimelineModel(self):
return model.TimelineModel(self._tracing_data)
class CategoryFilter(object):
......@@ -135,10 +138,14 @@ class TracingBackend(object):
self._category_filter = None
self._nesting = 0
self._tracing_data = []
self._tab_to_marker_mapping = {}
def _IsTracing(self):
return self._thread != None
def AddTabToMarkerMapping(self, tab, marker):
self._tab_to_marker_mapping[tab] = marker
def StartTracing(self, custom_categories=None, timeout=10):
""" Starts tracing on the first nested call and returns True. Returns False
and does nothing on subsequent nested calls.
......@@ -188,10 +195,14 @@ class TracingBackend(object):
def _GetTraceResult(self):
assert not self._IsTracing()
if self._tracing_data and type(self._tracing_data[0]) in [str, unicode]:
result_impl = TraceResultImpl(self._tracing_data)
result = trace_result.TraceResult(
ChromeLegacyTraceResult(self._tracing_data,
self._tab_to_marker_mapping))
else:
result_impl = RawTraceResultImpl(self._tracing_data)
return trace_result.TraceResult(result_impl)
result = trace_result.TraceResult(
ChromeRawTraceResult(self._tracing_data,
self._tab_to_marker_mapping))
return result
def _GetTraceResultAndReset(self):
result = self._GetTraceResult()
......
......@@ -80,26 +80,26 @@ class TracingBackendTest(tab_test_case.TabTestCase):
# is implemented (crbug.com/173327).
class TracingResultImplTest(unittest.TestCase):
class ChromeTraceResultTest(unittest.TestCase):
# Override TestCase.run to run a test with all possible
# implementations of TraceResult.
# implementations of ChromeTraceResult.
def __init__(self, method_name):
self._traceResultImplClass = None
super(TracingResultImplTest, self).__init__(method_name)
self._chromeTraceResultClass = None
super(ChromeTraceResultTest, self).__init__(method_name)
def run(self, result=None):
def RawTraceResultImplWrapper(strings):
return tracing_backend.RawTraceResultImpl(map(json.loads, strings))
def ChromeRawTraceResultWrapper(strings):
return tracing_backend.ChromeRawTraceResult(map(json.loads, strings))
classes = [
tracing_backend.TraceResultImpl,
RawTraceResultImplWrapper
tracing_backend.ChromeLegacyTraceResult,
ChromeRawTraceResultWrapper
]
for cls in classes:
self._traceResultImplClass = cls
super(TracingResultImplTest, self).run(result)
self._chromeTraceResultClass = cls
super(ChromeTraceResultTest, self).run(result)
def testWrite1(self):
ri = self._traceResultImplClass([])
ri = self._chromeTraceResultClass([])
f = cStringIO.StringIO()
ri.Serialize(f)
v = f.getvalue()
......@@ -109,7 +109,7 @@ class TracingResultImplTest(unittest.TestCase):
self.assertEquals(j['traceEvents'], [])
def testWrite2(self):
ri = self._traceResultImplClass([
ri = self._chromeTraceResultClass([
'"foo"',
'"bar"'])
f = cStringIO.StringIO()
......@@ -121,7 +121,7 @@ class TracingResultImplTest(unittest.TestCase):
self.assertEquals(j['traceEvents'], ['foo', 'bar'])
def testWrite3(self):
ri = self._traceResultImplClass([
ri = self._chromeTraceResultClass([
'"foo"',
'"bar"',
'"baz"'])
......
......@@ -10,6 +10,8 @@ https://code.google.com/p/trace-viewer/
from operator import attrgetter
import telemetry.core.timeline.process as tracing_process
from telemetry.core import web_contents
from telemetry.core import browser
# Register importers for data
from telemetry.core.timeline import inspector_importer
......@@ -41,6 +43,7 @@ class TimelineModel(object):
self._frozen = False
self.import_errors = []
self.metadata = []
self._core_object_to_timeline_container_map = {}
if event_data is not None:
self.ImportTraces([event_data], shift_world_to_zero=shift_world_to_zero)
......@@ -168,6 +171,18 @@ class TimelineModel(object):
return events
def GetRendererProcessFromTab(self, tab):
return self._core_object_to_timeline_container_map[tab]
def AddCoreObjectToContainerMapping(self, core_object, container):
""" Add a mapping from a core object to a timeline container.
Used for example to map a Tab to its renderer process in the timeline model.
"""
assert(isinstance(core_object, web_contents.WebContents) or
isinstance(core_object, browser.Browser))
self._core_object_to_timeline_container_map[core_object] = container
def _CreateImporter(self, event_data):
for importer_class in _IMPORTERS:
if importer_class.CanImport(event_data):
......
......@@ -60,5 +60,5 @@ class PageAction(object):
"""
raise Exception('This action cannot be bound.')
def GetTimelineMarkerLabel(self):
def GetTimelineMarkerName(self):
return None
......@@ -53,5 +53,5 @@ class PinchAction(page_action.PageAction):
window.__pinchAction.endMeasuringHook = function() { %s };
""" % (start_js, stop_js))
def GetTimelineMarkerLabel(self):
def GetTimelineMarkerName(self):
return 'SyntheticGestureController::running'
......@@ -82,5 +82,5 @@ class ScrollAction(page_action.PageAction):
window.__scrollAction.endMeasuringHook = function() { %s };
""" % (start_js, stop_js))
def GetTimelineMarkerLabel(self):
def GetTimelineMarkerName(self):
return 'SyntheticGestureController::running'
......@@ -19,7 +19,7 @@ class WaitAction(page_action.PageAction):
def RunAction(self, page, tab, previous_action):
tab.ExecuteJavaScript(
'console.time("' + self.GetTimelineMarkerLabel() + '")')
'console.time("' + self.GetTimelineMarkerName() + '")')
if hasattr(self, 'seconds'):
time.sleep(self.seconds)
......@@ -69,7 +69,7 @@ class WaitAction(page_action.PageAction):
raise page_action.PageActionFailed('No wait condition found')
tab.ExecuteJavaScript(
'console.timeEnd("' + self.GetTimelineMarkerLabel() + '")')
'console.timeEnd("' + self.GetTimelineMarkerName() + '")')
def GetTimelineMarkerLabel(self):
def GetTimelineMarkerName(self):
return 'WaitAction::RunAction'
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