Commit 030ae792 authored by nednguyen@google.com's avatar nednguyen@google.com

Remove the weak dictionary that maps tab objects to it markers in tracing_timeline_data.

We add the list of tab's markers to traces as metadata and later reconstruct the maps from tab markers to renderer thread in trace_event_importer. This makes it possible to reconstruct the timeline model with the mapping between tabs & renderer threads from the raw json tracing data. 

BUG=

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@275671 0039d316-1c4b-4281-b951-d872f2087c98
parent 0d9a2bd0
......@@ -31,7 +31,7 @@ class LoadingTrace(page_measurement.PageMeasurement):
loading.LoadingMetric().AddResults(tab, results)
timeline_metric = timeline.LoadTimesTimelineMetric()
renderer_thread = \
self._timeline_controller.model.GetRendererThreadFromTab(tab)
self._timeline_controller.model.GetRendererThreadFromTabId(tab.id)
record = tir_module.TimelineInteractionRecord(
"loading_trace_interaction", 0, float('inf'))
timeline_metric.AddResults(
......
......@@ -51,7 +51,8 @@ class SmoothnessController(object):
# the time ranges of gestures, if there is at least one, else the the time
# ranges from the first action to the last action.
renderer_thread = self._timeline_model.GetRendererThreadFromTab(tab)
renderer_thread = self._timeline_model.GetRendererThreadFromTabId(
tab.id)
run_smooth_actions_record = None
smooth_records = []
for event in renderer_thread.async_slices:
......
......@@ -39,7 +39,7 @@ class ThreadTimes(page_measurement.PageMeasurement):
def MeasurePage(self, page, tab, results):
metric = timeline.ThreadTimesTimelineMetric()
renderer_thread = \
self._timeline_controller.model.GetRendererThreadFromTab(tab)
self._timeline_controller.model.GetRendererThreadFromTabId(tab.id)
if self.options.report_silk_results:
metric.results_to_report = timeline.ReportSilkResults
if self.options.report_silk_details:
......
......@@ -47,8 +47,8 @@ class TimelineController(object):
# Stop tracing.
timeline_data = tab.browser.StopTracing()
self._model = TimelineModel(timeline_data)
self._renderer_process = self._model.GetRendererProcessFromTab(tab)
renderer_thread = self.model.GetRendererThreadFromTab(tab)
self._renderer_process = self._model.GetRendererProcessFromTabId(tab.id)
renderer_thread = self.model.GetRendererThreadFromTabId(tab.id)
run_smooth_actions_record = None
self._smooth_records = []
......
......@@ -24,6 +24,7 @@ from telemetry.core.backends.chrome import extension_backend
from telemetry.core.backends.chrome import system_info_backend
from telemetry.core.backends.chrome import tab_list_backend
from telemetry.core.backends.chrome import tracing_backend
from telemetry.core.backends.chrome import tracing_timeline_data
from telemetry.unittest import options_for_unittests
......@@ -282,17 +283,21 @@ class ChromeBrowserBackend(browser_backend.BrowserBackend):
def StopTracing(self):
""" Stops tracing and returns the result as TimelineData object. """
for (i, debugger_url) in enumerate(self._browser.tabs):
tab_ids_list = []
for (i, _) in enumerate(self._browser.tabs):
tab = self.tab_list_backend.Get(i, None)
if tab:
success = tab.EvaluateJavaScript(
"console.time('" + debugger_url + "');" +
"console.timeEnd('" + debugger_url + "');" +
"console.time('" + tab.id + "');" +
"console.timeEnd('" + tab.id + "');" +
"console.time.toString().indexOf('[native code]') != -1;")
if not success:
raise Exception('Page stomped on console.time')
self._tracing_backend.AddTabToMarkerMapping(tab, debugger_url)
return self._tracing_backend.StopTracing()
tab_ids_list.append(tab.id)
trace_events = self._tracing_backend.StopTracing()
# Augment tab_ids data to trace events.
event_data = {'traceEvents' : trace_events, 'tabIds': tab_ids_list}
return tracing_timeline_data.TracingTimelineData(event_data)
def GetProcessName(self, cmd_line):
"""Returns a user-friendly name for the process of the given |cmd_line|."""
......
......@@ -78,6 +78,10 @@ class InspectorBackend(inspector_websocket.InspectorWebsocket):
return c['url']
return None
@property
def id(self):
return self.debugger_url
@property
def debugger_url(self):
return self._context['webSocketDebuggerUrl']
......
......@@ -4,10 +4,8 @@
import logging
import re
import weakref
from telemetry.core.backends.chrome import inspector_websocket
from telemetry.core.backends.chrome import tracing_timeline_data
# All tracing categories not disabled-by-default
......@@ -109,10 +107,6 @@ class TracingBackend(object):
self._category_filter = None
self._nesting = 0
self._tracing_data = []
# Use a WeakKeyDictionary, because an ordinary dictionary could keep
# references to Tab objects around until it gets garbage collected.
# This would prevent telemetry from navigating to another page.
self._tab_to_marker_mapping = weakref.WeakKeyDictionary()
@property
def is_tracing_running(self):
......@@ -164,14 +158,11 @@ class TracingBackend(object):
def _GetTraceResult(self):
assert not self.is_tracing_running
return tracing_timeline_data.TracingTimelineData(
self._tracing_data, self._tab_to_marker_mapping)
return self._tracing_data
def _GetTraceResultAndReset(self):
result = self._GetTraceResult()
# Reset tab to marker mapping for the next tracing run. Don't use clear(),
# because a TraceResult may still hold a reference to the dictionary object.
self._tab_to_marker_mapping = weakref.WeakKeyDictionary()
self._tracing_data = []
return result
......
......@@ -3,36 +3,20 @@
# found in the LICENSE file.
import json
import weakref
from telemetry.core.timeline_data import TimelineData
class TracingTimelineData(TimelineData):
def __init__(self, event_data, tab_to_marker_mapping = None):
def __init__(self, event_data):
super(TracingTimelineData, self).__init__()
self._event_data = event_data
if tab_to_marker_mapping == None:
self._tab_to_marker_mapping = weakref.WeakKeyDictionary()
else:
self._tab_to_marker_mapping = tab_to_marker_mapping
def Serialize(self, f):
"""Serializes the trace result to a file-like object"""
f.write('{"traceEvents":')
json.dump(self._event_data, f)
f.write('}')
if 'traceEvents' in self._event_data:
json.dump(self._event_data, f)
else:
json.dump({'traceEvents' : self._event_data}, f)
def EventData(self):
return self._event_data
def AugmentTimelineModel(self, timeline):
for thread in timeline.GetAllThreads():
if thread.name == 'CrBrowserMain':
timeline.browser_process = thread.parent
for key, value in self._tab_to_marker_mapping.iteritems():
timeline_markers = timeline.FindTimelineMarkers(value)
assert(len(timeline_markers) == 1)
assert(timeline_markers[0].start_thread ==
timeline_markers[0].end_thread)
renderer_thread = timeline_markers[0].start_thread
timeline.AddCoreObjectToContainerMapping(key, renderer_thread)
......@@ -104,6 +104,10 @@ class WebDriverTabBackend(object):
# IE/Firefox has no timeline.
raise NotImplementedError()
@property
def id(self):
raise NotImplementedError()
def StartTimelineRecording(self):
raise NotImplementedError()
......
......@@ -103,7 +103,8 @@ class TabTest(tab_test_case.TabTestCase):
self._tab.ClearHighlight(bitmap.WEB_PAGE_TEST_ORANGE)
trace_data = self._browser.StopTracing()
timeline_model = model.TimelineModel(trace_data)
renderer_thread = timeline_model.GetRendererThreadFromTab(self._tab)
renderer_thread = timeline_model.GetRendererThreadFromTabId(
self._tab.id)
found_video_start_event = False
for event in renderer_thread.async_slices:
if event.name == '__ClearHighlight.video_capture_start':
......@@ -111,6 +112,47 @@ class TabTest(tab_test_case.TabTestCase):
break
self.assertTrue(found_video_start_event)
def testGetRendererThreadFromTabId(self):
self.assertEquals(self._tab.url, 'about:blank')
# Create 3 tabs. The third tab is closed before we call StartTracing.
first_tab = self._tab
second_tab = self._browser.tabs.New()
second_tab.Navigate('about:blank')
second_tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
third_tab = self._browser.tabs.New()
third_tab.Navigate('about:blank')
third_tab.WaitForDocumentReadyStateToBeInteractiveOrBetter()
third_tab.Close()
self._browser.StartTracing(tracing_backend.MINIMAL_TRACE_CATEGORIES)
first_tab.ExecuteJavaScript('console.time("first-tab-marker");')
first_tab.ExecuteJavaScript('console.timeEnd("first-tab-marker");')
second_tab.ExecuteJavaScript('console.time("second-tab-marker");')
second_tab.ExecuteJavaScript('console.timeEnd("second-tab-marker");')
trace_data = self._browser.StopTracing()
timeline_model = model.TimelineModel(trace_data)
# Assert that the renderer_thread of the first tab contains
# 'first-tab-marker'.
renderer_thread = timeline_model.GetRendererThreadFromTabId(
first_tab.id)
first_tab_markers = [
renderer_thread.IterAllSlicesOfName('first-tab-marker')]
self.assertEquals(1, len(first_tab_markers))
# Close second tab and assert that the renderer_thread of the second tab
# contains 'second-tab-marker'.
second_tab.Close()
renderer_thread = timeline_model.GetRendererThreadFromTabId(
second_tab.id)
second_tab_markers = [
renderer_thread.IterAllSlicesOfName('second-tab-marker')]
self.assertEquals(1, len(second_tab_markers))
# Third tab wasn't available when we start tracing, so there is no
# renderer_thread corresponding to it in the the trace.
self.assertIs(None, timeline_model.GetRendererThreadFromTabId(third_tab.id))
class GpuTabTest(tab_test_case.TabTestCase):
def setUp(self):
......
......@@ -8,11 +8,8 @@ https://code.google.com/p/trace-viewer/
'''
from operator import attrgetter
import weakref
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 bounds
......@@ -52,14 +49,10 @@ class TimelineModel(object):
self._processes = {}
self._browser_process = None
self._frozen = False
self._tab_ids_to_renderer_threads_map = {}
self.import_errors = []
self.metadata = []
self.flow_events = []
# Use a WeakKeyDictionary, because an ordinary dictionary could keep
# references to Tab objects around until it gets garbage collected.
# This would prevent telemetry from navigating to another page.
self._core_object_to_timeline_container_map = weakref.WeakKeyDictionary()
if timeline_data is not None:
self.ImportTraces(timeline_data, shift_world_to_zero=shift_world_to_zero)
......@@ -85,9 +78,15 @@ class TimelineModel(object):
def browser_process(self, browser_process):
self._browser_process = browser_process
def AddMappingFromTabIdToRendererThread(self, tab_id, renderer_thread):
if self._frozen:
raise Exception('Cannot add mapping from tab id to renderer thread once '
'trace is imported')
self._tab_ids_to_renderer_threads_map[tab_id] = renderer_thread
def ImportTraces(self, timeline_data, shift_world_to_zero=True):
if self._frozen:
raise Exception("Cannot add events once recording is done")
raise Exception("Cannot add events once trace is imported")
importers = []
if isinstance(timeline_data, list):
......@@ -232,20 +231,14 @@ class TimelineModel(object):
return events
def GetRendererProcessFromTab(self, tab):
return self._core_object_to_timeline_container_map[tab].parent
def GetRendererProcessFromTabId(self, tab_id):
renderer_thread = self.GetRendererThreadFromTabId(tab_id)
if renderer_thread:
return renderer_thread.parent
return None
def GetRendererThreadFromTab(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 GetRendererThreadFromTabId(self, tab_id):
return self._tab_ids_to_renderer_threads_map.get(tab_id, None)
def _CreateImporter(self, event_data):
for importer_class in _IMPORTERS:
......
......@@ -16,6 +16,7 @@ from telemetry.core.timeline import importer
import telemetry.core.timeline.async_slice as tracing_async_slice
import telemetry.core.timeline.flow_event as tracing_flow_event
class TraceEventTimelineImporter(importer.TimelineImporter):
def __init__(self, model, timeline_data):
super(TraceEventTimelineImporter, self).__init__(
......@@ -58,7 +59,7 @@ class TraceEventTimelineImporter(importer.TimelineImporter):
# Any other fields in the container should be treated as metadata.
self._model.metadata.append({
'name' : field_name,
'value' : container['field_name']})
'value' : container[field_name]})
@staticmethod
def CanImport(timeline_data):
......@@ -275,10 +276,10 @@ class TraceEventTimelineImporter(importer.TimelineImporter):
self._model.UpdateBounds()
self._CreateAsyncSlices()
self._CreateFlowSlices()
self._SetBrowserProcess()
self._CreateExplicitObjects()
self._CreateImplicitObjects()
self._timeline_data.AugmentTimelineModel(self._model)
self._CreateTabIdsToThreadsMap()
def _CreateAsyncSlices(self):
if len(self._all_async_events) == 0:
......@@ -445,3 +446,22 @@ class TraceEventTimelineImporter(importer.TimelineImporter):
else:
# Make this event the next start event in this flow.
flow_id_to_event[event['id']] = flow_event
def _SetBrowserProcess(self):
for thread in self._model.GetAllThreads():
if thread.name == 'CrBrowserMain':
self._model.browser_process = thread.parent
def _CreateTabIdsToThreadsMap(self):
tab_ids_list = []
for metadata in self._model.metadata:
if metadata['name'] == 'tabIds':
tab_ids_list = metadata['value']
break
for tab_id in tab_ids_list:
timeline_markers = self._model.FindTimelineMarkers(tab_id)
assert(len(timeline_markers) == 1)
assert(timeline_markers[0].start_thread ==
timeline_markers[0].end_thread)
self._model.AddMappingFromTabIdToRendererThread(
tab_id, timeline_markers[0].start_thread)
......@@ -1152,3 +1152,38 @@ class TraceEventTimelineImporterTest(unittest.TestCase):
timeline_data = tracing_timeline_data.TracingTimelineData(events)
m = timeline_model.TimelineModel(timeline_data=timeline_data)
self.assertEqual(0, len(m.flow_events))
def testTraceEventsWithTabIdsMarkers(self):
trace_events = [
{'name': 'a', 'args': {}, 'pid': 1, 'ts': 20, 'tts': 10, 'cat': 'foo',
'tid': 1, 'ph': 'B'},
# tab-id-1
{'name': 'tab-id-1', 'args': {}, 'pid': 1, 'ts': 25, 'cat': 'foo',
'tid': 1,
'ph': 'S', 'id': 72},
{'name': 'a', 'args': {}, 'pid': 1, 'ts': 30, 'tts': 20, 'cat': 'foo',
'tid': 1, 'ph': 'E'},
{'name': 'tab-id-1', 'args': {}, 'pid': 1, 'ts': 35, 'cat': 'foo',
'tid': 1,
'ph': 'F', 'id': 72},
# tab-id-2
{'name': 'tab-id-2', 'args': {}, 'pid': 1, 'ts': 25, 'cat': 'foo',
'tid': 2,
'ph': 'S', 'id': 72},
{'name': 'tab-id-2', 'args': {}, 'pid': 1, 'ts': 26, 'cat': 'foo',
'tid': 2,
'ph': 'F', 'id': 72},
]
event_data = {'traceEvents': trace_events,
'tabIds': ['tab-id-1', 'tab-id-2']}
timeline_data = tracing_timeline_data.TracingTimelineData(event_data)
m = timeline_model.TimelineModel(timeline_data=timeline_data)
processes = m.GetAllProcesses()
self.assertEqual(1, len(processes))
self.assertIs(processes[0], m.GetRendererProcessFromTabId('tab-id-1'))
self.assertIs(processes[0], m.GetRendererProcessFromTabId('tab-id-2'))
p = processes[0]
self.assertEqual(2, len(p.threads))
self.assertIs(p.threads[1], m.GetRendererThreadFromTabId('tab-id-1'))
self.assertIs(p.threads[2], m.GetRendererThreadFromTabId('tab-id-2'))
......@@ -20,6 +20,11 @@ class WebContents(object):
'network_quiescence.js')) as f:
self._quiescence_js = f.read()
@property
def id(self):
"""Return the unique id string for this tab object."""
return self._inspector_backend.id
def WaitForDocumentReadyStateToBeComplete(self,
timeout=DEFAULT_WEB_CONTENTS_TIMEOUT):
self.WaitForJavaScriptExpression(
......
......@@ -24,7 +24,7 @@ class ActionRunnerTest(tab_test_case.TabTestCase):
timeline_model = model.TimelineModel(trace_data)
records = []
renderer_thread = timeline_model.GetRendererThreadFromTab(self._tab)
renderer_thread = timeline_model.GetRendererThreadFromTabId(self._tab.id)
for event in renderer_thread.async_slices:
if not tir_module.IsTimelineInteractionRecord(event.name):
continue
......
......@@ -151,7 +151,7 @@ class TimelineBasedMeasurement(page_measurement.PageMeasurement):
logging.error('Cannot open %s. %s' % (trace_file_path, e))
model = model_module.TimelineModel(trace_result)
renderer_thread = model.GetRendererThreadFromTab(tab)
renderer_thread = model.GetRendererThreadFromTabId(tab.id)
meta_metrics = _TimelineBasedMetrics(
model, renderer_thread, self.CreateMetricsForTimelineInteractionRecord)
meta_metrics.AddResults(results)
......
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