Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: tools/android/loading/process_request_log.py

Issue 1619713002: Upgrade analyze.py and related scripts to new world order. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: comments Created 4 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « tools/android/loading/log_requests.py ('k') | tools/android/loading/processing.py » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 #! /usr/bin/python
2 # Copyright 2015 The Chromium Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
5
6 """Creates a Graphviz file visualizing the resource dependencies from a JSON
7 file dumped by log_requests.py.
8 """
9
10 import collections
11 import sys
12 import urlparse
13
14 import log_parser
15 from log_parser import Resource
16
17
18 def _BuildResourceDependencyGraph(requests):
19 """Builds the graph of resource dependencies.
20
21 Args:
22 requests: [RequestData, ...]
23
24 Returns:
25 A tuple ([Resource], [(resource1, resource2, reason), ...])
26 """
27 resources = log_parser.GetResources(requests)
28 resources_from_url = {resource.url: resource for resource in resources}
29 requests_by_completion = log_parser.SortedByCompletion(requests)
30 deps = []
31 for r in requests:
32 resource = Resource.FromRequest(r)
33 initiator = r.initiator
34 initiator_type = initiator['type']
35 dep = None
36 if initiator_type == 'parser':
37 url = initiator['url']
38 blocking_resource = resources_from_url.get(url, None)
39 if blocking_resource is None:
40 continue
41 dep = (blocking_resource, resource, 'parser')
42 elif initiator_type == 'script' and 'stackTrace' in initiator:
43 for frame in initiator['stackTrace']:
44 url = frame['url']
45 blocking_resource = resources_from_url.get(url, None)
46 if blocking_resource is None:
47 continue
48 dep = (blocking_resource, resource, 'stack')
49 break
50 else:
51 # When the initiator is a script without a stackTrace, infer that it comes
52 # from the most recent script from the same hostname.
53 # TLD+1 might be better, but finding what is a TLD requires a database.
54 request_hostname = urlparse.urlparse(r.url).hostname
55 sorted_script_requests_from_hostname = [
56 r for r in requests_by_completion
57 if (resource.GetContentType() in ('script', 'html', 'json')
58 and urlparse.urlparse(r.url).hostname == request_hostname)]
59 most_recent = None
60 # Linear search is bad, but this shouldn't matter here.
61 for request in sorted_script_requests_from_hostname:
62 if request.timestamp < r.timing.requestTime:
63 most_recent = request
64 else:
65 break
66 if most_recent is not None:
67 blocking = resources_from_url.get(most_recent.url, None)
68 if blocking is not None:
69 dep = (blocking, resource, 'script_inferred')
70 if dep is not None:
71 deps.append(dep)
72 return (resources, deps)
73
74
75 def PrefetchableResources(requests):
76 """Returns a list of resources that are discoverable without JS.
77
78 Args:
79 requests: List of requests.
80
81 Returns:
82 List of discoverable resources, with their initial request.
83 """
84 resource_to_request = log_parser.ResourceToRequestMap(requests)
85 (_, all_deps) = _BuildResourceDependencyGraph(requests)
86 # Only keep "parser" arcs
87 deps = [(first, second) for (first, second, reason) in all_deps
88 if reason == 'parser']
89 deps_per_resource = collections.defaultdict(list)
90 for (first, second) in deps:
91 deps_per_resource[first].append(second)
92 result = []
93 visited = set()
94 to_visit = [deps[0][0]]
95 while len(to_visit) != 0:
96 r = to_visit.pop()
97 visited.add(r)
98 to_visit += deps_per_resource[r]
99 result.append(resource_to_request[r])
100 return result
101
102
103 _CONTENT_TYPE_TO_COLOR = {'html': 'red', 'css': 'green', 'script': 'blue',
104 'json': 'purple', 'gif_image': 'grey',
105 'image': 'orange', 'other': 'white'}
106
107
108 def _ResourceGraphvizNode(resource, request, resource_to_index):
109 """Returns the node description for a given resource.
110
111 Args:
112 resource: Resource.
113 request: RequestData associated with the resource.
114 resource_to_index: {Resource: int}.
115
116 Returns:
117 A string describing the resource in graphviz format.
118 The resource is color-coded according to its content type, and its shape is
119 oval if its max-age is less than 300s (or if it's not cacheable).
120 """
121 color = _CONTENT_TYPE_TO_COLOR[resource.GetContentType()]
122 max_age = log_parser.MaxAge(request)
123 shape = 'polygon' if max_age > 300 else 'oval'
124 return ('%d [label = "%s"; style = "filled"; fillcolor = %s; shape = %s];\n'
125 % (resource_to_index[resource], resource.GetShortName(), color,
126 shape))
127
128
129 def _GraphvizFileFromDeps(resources, requests, deps, output_filename):
130 """Writes a graphviz file from a set of resource dependencies.
131
132 Args:
133 resources: [Resource, ...]
134 requests: list of requests
135 deps: [(resource1, resource2, reason), ...]
136 output_filename: file to write the graph to.
137 """
138 with open(output_filename, 'w') as f:
139 f.write("""digraph dependencies {
140 rankdir = LR;
141 """)
142 resource_to_request = log_parser.ResourceToRequestMap(requests)
143 resource_to_index = {r: i for (i, r) in enumerate(resources)}
144 resources_with_edges = set()
145 for (first, second, reason) in deps:
146 resources_with_edges.add(first)
147 resources_with_edges.add(second)
148 if len(resources_with_edges) != len(resources):
149 f.write("""subgraph cluster_orphans {
150 color=black;
151 label="Orphans";
152 """)
153 for resource in resources:
154 if resource not in resources_with_edges:
155 request = resource_to_request[resource]
156 f.write(_ResourceGraphvizNode(resource, request, resource_to_index))
157 f.write('}\n')
158
159 f.write("""subgraph cluster_nodes {
160 color=invis;
161 """)
162 for resource in resources:
163 request = resource_to_request[resource]
164 print resource.url
165 if resource in resources_with_edges:
166 f.write(_ResourceGraphvizNode(resource, request, resource_to_index))
167 for (first, second, reason) in deps:
168 arrow = ''
169 if reason == 'parser':
170 arrow = '[color = red]'
171 elif reason == 'stack':
172 arrow = '[color = blue]'
173 elif reason == 'script_inferred':
174 arrow = '[color = blue; style=dotted]'
175 f.write('%d -> %d %s;\n' % (
176 resource_to_index[first], resource_to_index[second], arrow))
177 f.write('}\n}\n')
178
179
180 def main():
181 filename = sys.argv[1]
182 requests = log_parser.ParseJsonFile(filename)
183 requests = log_parser.FilterRequests(requests)
184 (resources, deps) = _BuildResourceDependencyGraph(requests)
185 _GraphvizFileFromDeps(resources, requests, deps, filename + '.dot')
186
187
188 if __name__ == '__main__':
189 main()
OLDNEW
« no previous file with comments | « tools/android/loading/log_requests.py ('k') | tools/android/loading/processing.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698