| Index: tools/android/loading/loading_model.py
|
| diff --git a/tools/android/loading/loading_model.py b/tools/android/loading/loading_model.py
|
| index fed48e4b7048ad2ac28b8602c3b487ed88a17b2f..7ba75fc79b4bfa41a8e738d23bd71fa99d3484d0 100644
|
| --- a/tools/android/loading/loading_model.py
|
| +++ b/tools/android/loading/loading_model.py
|
| @@ -6,11 +6,11 @@
|
|
|
| (Redirect the following to the general model module once we have one)
|
| A model is an object with the following methods.
|
| - CostMs(): return the cost of the cost in milliseconds.
|
| - Set(): set model-specifical parameters.
|
| + CostMs(): return the cost of the model in milliseconds.
|
| + Set(): set model-specific parameters.
|
|
|
| ResourceGraph
|
| - This creates a DAG of resource dependancies from loading.log_requests to model
|
| + This creates a DAG of resource dependencies from loading.log_requests to model
|
| loading time. The model may be parameterized by changing the loading time of
|
| a particular or all resources.
|
| """
|
| @@ -21,7 +21,7 @@ import urlparse
|
| import sys
|
|
|
| import dag
|
| -import log_parser
|
| +import request_dependencies_lens
|
|
|
| class ResourceGraph(object):
|
| """A model of loading by a DAG (tree?) of resource dependancies.
|
| @@ -29,14 +29,13 @@ class ResourceGraph(object):
|
| Set parameters:
|
| cache_all: if true, assume zero loading time for all resources.
|
| """
|
| -
|
| - def __init__(self, requests):
|
| - """Create from a parsed request set.
|
| + def __init__(self, trace):
|
| + """Create from a LoadingTrace.
|
|
|
| Args:
|
| - requests: [RequestData, ...] filtered RequestData from loading.log_parser.
|
| + trace: (LoadingTrace) Loading trace.
|
| """
|
| - self._BuildDag(requests)
|
| + self._BuildDag(trace)
|
| self._global_start = min([n.StartTime() for n in self._node_info])
|
| # Sort before splitting children so that we can correctly dectect if a
|
| # reparented child is actually a dependency for a child of its new parent.
|
| @@ -182,7 +181,7 @@ class ResourceGraph(object):
|
| while n.Predecessors():
|
| n = reduce(lambda costliest, next:
|
| next if (self._node_filter(next) and
|
| - cost[next.Index()] > cost[costliest.Index()])
|
| + costs[next.Index()] > costs[costliest.Index()])
|
| else costliest,
|
| n.Predecessors())
|
| path_list.insert(0, self._node_info[n.Index()])
|
| @@ -322,10 +321,10 @@ class ResourceGraph(object):
|
| self._node = node
|
| self._edge_costs = {}
|
| self._edge_annotations = {}
|
| - # All fields in timing are millis relative to requestTime, which is epoch
|
| + # All fields in timing are millis relative to request_time, which is epoch
|
| # seconds.
|
| self._node_cost = max([t for f, t in request.timing._asdict().iteritems()
|
| - if f != 'requestTime'])
|
| + if f != 'request_time'])
|
|
|
| def __str__(self):
|
| return self.ShortName()
|
| @@ -346,20 +345,37 @@ class ResourceGraph(object):
|
| return self._edge_costs[s]
|
|
|
| def StartTime(self):
|
| - return self._request.timing.requestTime * 1000
|
| + return self._request.timing.request_time * 1000
|
|
|
| def EndTime(self):
|
| - return self._request.timing.requestTime * 1000 + self._node_cost
|
| + return self._request.timing.request_time * 1000 + self._node_cost
|
|
|
| def EdgeAnnotation(self, s):
|
| assert s.Node() in self.Node().Successors()
|
| return self._edge_annotations.get(s, [])
|
|
|
| def ContentType(self):
|
| - return log_parser.Resource.FromRequest(self._request).GetContentType()
|
| + return self._request.GetContentType()
|
|
|
| def ShortName(self):
|
| - return log_parser.Resource.FromRequest(self._request).GetShortName()
|
| + """Returns either the hostname of the resource, or the filename,
|
| + or the end of the path. Tries to include the domain as much as possible.
|
| + """
|
| + parsed = urlparse.urlparse(self._request.url)
|
| + path = parsed.path
|
| + if path != '' and path != '/':
|
| + last_path = parsed.path.split('/')[-1]
|
| + if len(last_path) < 10:
|
| + if len(path) < 10:
|
| + return parsed.hostname + '/' + path
|
| + else:
|
| + return parsed.hostname + '/..' + parsed.path[-10:]
|
| + elif len(last_path) > 10:
|
| + return parsed.hostname + '/..' + last_path[:5]
|
| + else:
|
| + return parsed.hostname + '/..' + last_path
|
| + else:
|
| + return parsed.hostname
|
|
|
| def Url(self):
|
| return self._request.url
|
| @@ -422,7 +438,7 @@ class ResourceGraph(object):
|
| return self._node_info[parent.Index()].EdgeAnnotation(
|
| self._node_info[child.Index()])
|
|
|
| - def _BuildDag(self, requests):
|
| + def _BuildDag(self, trace):
|
| """Build DAG of resources.
|
|
|
| Build a DAG from our requests and augment with _NodeInfo (see above) in a
|
| @@ -431,112 +447,36 @@ class ResourceGraph(object):
|
| Creates self._nodes and self._node_info.
|
|
|
| Args:
|
| - requests: [Request, ...] Requests from loading.log_parser.
|
| + trace: A LoadingTrace.
|
| """
|
| self._nodes = []
|
| self._node_info = []
|
| - indicies_by_url = {}
|
| - requests_by_completion = log_parser.SortedByCompletion(requests)
|
| - for request in requests:
|
| + index_by_request = {}
|
| + for request in trace.request_track.GetEvents():
|
| next_index = len(self._nodes)
|
| - indicies_by_url.setdefault(request.url, []).append(next_index)
|
| + assert request not in index_by_request
|
| + index_by_request[request] = next_index
|
| node = dag.Node(next_index)
|
| node_info = self._NodeInfo(node, request)
|
| self._nodes.append(node)
|
| self._node_info.append(node_info)
|
| - for url, indicies in indicies_by_url.iteritems():
|
| - if len(indicies) > 1:
|
| - logging.warning('Multiple loads (%d) for url: %s' %
|
| - (len(indicies), url))
|
| - for i in xrange(len(requests)):
|
| - request = requests[i]
|
| - current_node_info = self._node_info[i]
|
| - resource = log_parser.Resource.FromRequest(current_node_info.Request())
|
| - initiator = request.initiator
|
| - initiator_type = initiator['type']
|
| - predecessor_url = None
|
| - predecessor_type = None
|
| - # Classify & infer the predecessor. If a candidate url we identify as the
|
| - # predecessor is not in index_by_url, then we haven't seen it in our
|
| - # requests and we will try to find a better predecessor.
|
| - if initiator_type == 'parser':
|
| - url = initiator['url']
|
| - if url in indicies_by_url:
|
| - predecessor_url = url
|
| - predecessor_type = 'parser'
|
| - elif initiator_type == 'script' and 'stackTrace' in initiator:
|
| - for frame in initiator['stackTrace']:
|
| - url = frame['url']
|
| - if url in indicies_by_url:
|
| - predecessor_url = url
|
| - predecessor_type = 'stack'
|
| - break
|
| - elif initiator_type == 'script':
|
| - # When the initiator is a script without a stackTrace, infer that it
|
| - # comes from the most recent script from the same hostname. TLD+1 might
|
| - # be better, but finding what is a TLD requires a database.
|
| - request_hostname = urlparse.urlparse(request.url).hostname
|
| - sorted_script_requests_from_hostname = [
|
| - r for r in requests_by_completion
|
| - if (resource.GetContentType() in ('script', 'html', 'json')
|
| - and urlparse.urlparse(r.url).hostname == request_hostname)]
|
| - most_recent = None
|
| - # Linear search is bad, but this shouldn't matter here.
|
| - for r in sorted_script_requests_from_hostname:
|
| - if r.timestamp < request.timing.requestTime:
|
| - most_recent = r
|
| - else:
|
| - break
|
| - if most_recent is not None:
|
| - url = most_recent.url
|
| - if url in indicies_by_url:
|
| - predecessor_url = url
|
| - predecessor_type = 'script_inferred'
|
| - # TODO(mattcary): we skip initiator type other, is that correct?
|
| - if predecessor_url is not None:
|
| - predecessor = self._FindBestPredecessor(
|
| - current_node_info, indicies_by_url[predecessor_url])
|
| - edge_cost = current_node_info.StartTime() - predecessor.EndTime()
|
| - if edge_cost < 0:
|
| - edge_cost = 0
|
| - if current_node_info.StartTime() < predecessor.StartTime():
|
| - logging.error('Inverted dependency: %s->%s',
|
| - predecessor.ShortName(), current_node_info.ShortName())
|
| - # Note that current.StartTime() < predecessor.EndTime() appears to
|
| - # happen a fair amount in practice.
|
| - predecessor.Node().AddSuccessor(current_node_info.Node())
|
| - predecessor.SetEdgeCost(current_node_info, edge_cost)
|
| - predecessor.AddEdgeAnnotation(current_node_info, predecessor_type)
|
| -
|
| - def _FindBestPredecessor(self, node_info, candidate_indicies):
|
| - """Find best predecessor for node_info
|
| -
|
| - If there is only one candidate, we use it regardless of timings. We will
|
| - later warn about inverted dependencies. If there are more than one, we use
|
| - the latest whose end time is before node_info's start time. If there is no
|
| - such candidate, we throw up our hands and return an arbitrary one.
|
| -
|
| - Args:
|
| - node_info: _NodeInfo of interest.
|
| - candidate_indicies: indicies of candidate predecessors.
|
| -
|
| - Returns:
|
| - _NodeInfo of best predecessor.
|
| - """
|
| - assert candidate_indicies
|
| - if len(candidate_indicies) == 1:
|
| - return self._node_info[candidate_indicies[0]]
|
| - candidate = self._node_info[candidate_indicies[0]]
|
| - for i in xrange(1, len(candidate_indicies)):
|
| - next_candidate = self._node_info[candidate_indicies[i]]
|
| - if (next_candidate.EndTime() < node_info.StartTime() and
|
| - next_candidate.StartTime() > candidate.StartTime()):
|
| - candidate = next_candidate
|
| - if candidate.EndTime() > node_info.StartTime():
|
| - logging.warning('Multiple candidates but all inverted for ' +
|
| - node_info.ShortName())
|
| - return candidate
|
|
|
| + dependencies = request_dependencies_lens.RequestDependencyLens(
|
| + trace).GetRequestDependencies()
|
| + for child_rq, parent_rq, reason in dependencies:
|
| + parent = self._node_info[index_by_request[parent_rq]]
|
| + child = self._node_info[index_by_request[child_rq]]
|
| + edge_cost = child.StartTime() - parent.EndTime()
|
| + if edge_cost < 0:
|
| + edge_cost = 0
|
| + if child.StartTime() < parent.StartTime():
|
| + logging.error('Inverted dependency: %s->%s',
|
| + parent.ShortName(), child.ShortName())
|
| + # Note that child.StartTime() < parent.EndTime() appears to happen a
|
| + # fair amount in practice.
|
| + parent.Node().AddSuccessor(child.Node())
|
| + parent.SetEdgeCost(child, edge_cost)
|
| + parent.AddEdgeAnnotation(child, reason)
|
|
|
| def _SplitChildrenByTime(self, parent):
|
| """Split children of a node by request times.
|
| @@ -624,7 +564,7 @@ class ResourceGraph(object):
|
| """
|
| node_info = self._node_info[index]
|
| color = self._CONTENT_TYPE_TO_COLOR[node_info.ContentType()]
|
| - max_age = log_parser.MaxAge(node_info.Request())
|
| + max_age = node_info.Request().MaxAge()
|
| shape = 'polygon' if max_age > 300 else 'oval'
|
| styles = ['filled']
|
| if highlight:
|
|
|