blob: e0479abfda3d3ab09b5a09d0e546f7f0d98d1eac [file] [log] [blame]
# Copyright 2016 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.
"""Gathers and infers dependencies between requests.
When executed as a script, loads a trace and outputs synthetic frame load nodes
and the new introduced dependencies.
"""
import bisect
import collections
import logging
import operator
import loading_trace
class FrameLoadLens(object):
"""Analyses and creates request dependencies for inferred frame events."""
_FRAME_EVENT = 'RenderFrameImpl::didFinishLoad'
_REQUEST_TO_LOAD_GAP_MSEC = 100
_LOAD_TO_REQUEST_GAP_MSEC = 100
def __init__(self, trace):
"""Instance initialization.
Args:
trace: (LoadingTrace) Loading trace.
"""
self._frame_load_events = self._GetFrameLoadEvents(trace.tracing_track)
self._request_track = trace.request_track
self._tracing_track = trace.tracing_track
self._load_dependencies = []
self._request_dependencies = []
for i, load in enumerate(self._frame_load_events):
self._load_dependencies.extend(
[(i, r) for r in self._GetLoadDependencies(load)])
self._request_dependencies.extend(
[(r, i) for r in self._GetRequestDependencies(load)])
def GetFrameLoadInfo(self):
"""Returns [(index, msec)]."""
return [collections.namedtuple('LoadInfo', ['index', 'msec'])._make(
(i, self._frame_load_events[i].start_msec))
for i in xrange(len(self._frame_load_events))]
def GetFrameResourceComplete(self, request_track):
"""Returns [(frame id, msec)]."""
frame_to_end_msec = collections.defaultdict(int)
for r in request_track.GetEvents():
if r.end_msec > frame_to_end_msec[r.frame_id]:
frame_to_end_msec[r.frame_id] = r.end_msec
loads = []
for f in sorted(frame_to_end_msec.keys()):
loads.append((f, frame_to_end_msec[f]))
return loads
def GetFrameLoadDependencies(self):
"""Returns a list of frame load dependencies.
Returns:
([(frame load index, request), ...],
[(request, frame load index), ...]), where request are instances of
request_trace.Request, and frame load index is an integer. The first list
in the tuple gives the requests that are dependent on the given frame
load, and the second list gives the frame loads that are dependent on the
given request.
"""
return (self._load_dependencies, self._request_dependencies)
def _GetFrameLoadEvents(self, tracing_track):
events = []
for e in tracing_track.GetEvents():
if e.tracing_event['name'] == self._FRAME_EVENT:
events.append(e)
return events
def _GetLoadDependencies(self, load):
for r in self._request_track.GetEventsStartingBetween(
load.start_msec, load.start_msec + self._LOAD_TO_REQUEST_GAP_MSEC):
yield r
def _GetRequestDependencies(self, load):
for r in self._request_track.GetEventsEndingBetween(
load.start_msec - self._REQUEST_TO_LOAD_GAP_MSEC, load.start_msec):
yield r
if __name__ == '__main__':
import loading_trace
import json
import sys
lens = FrameLoadLens(loading_trace.LoadingTrace.FromJsonDict(
json.load(open(sys.argv[1]))))
load_times = lens.GetFrameLoadInfo()
for t in load_times:
print t
print (lens._request_track.GetFirstRequestMillis(),
lens._request_track.GetLastRequestMillis())
load_dep, request_dep = lens.GetFrameLoadDependencies()
rq_str = lambda r: '%s (%d-%d)' % (
r.request_id,
r.start_msec - lens._request_track.GetFirstRequestMillis(),
r.end_msec - lens._request_track.GetFirstRequestMillis())
load_str = lambda i: '%s (%d)' % (i, load_times[i][1])
for load_idx, request in load_dep:
print '%s -> %s' % (load_str(load_idx), rq_str(request))
for request, load_idx in request_dep:
print '%s -> %s' % (rq_str(request), load_str(load_idx))