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

Side by Side Diff: tools/android/loading/loading_model.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
OLDNEW
1 # Copyright 2015 The Chromium Authors. All rights reserved. 1 # Copyright 2015 The Chromium Authors. All rights reserved.
2 # Use of this source code is governed by a BSD-style license that can be 2 # Use of this source code is governed by a BSD-style license that can be
3 # found in the LICENSE file. 3 # found in the LICENSE file.
4 4
5 """Models for loading in chrome. 5 """Models for loading in chrome.
6 6
7 (Redirect the following to the general model module once we have one) 7 (Redirect the following to the general model module once we have one)
8 A model is an object with the following methods. 8 A model is an object with the following methods.
9 CostMs(): return the cost of the model in milliseconds. 9 CostMs(): return the cost of the model in milliseconds.
10 Set(): set model-specific parameters. 10 Set(): set model-specific parameters.
11 11
12 ResourceGraph 12 ResourceGraph
13 This creates a DAG of resource dependencies from loading.log_requests to model 13 This creates a DAG of resource dependencies from loading.log_requests to model
14 loading time. The model may be parameterized by changing the loading time of 14 loading time. The model may be parameterized by changing the loading time of
15 a particular or all resources. 15 a particular or all resources.
16 """ 16 """
17 17
18 import logging 18 import logging
19 import os 19 import os
20 import urlparse 20 import urlparse
21 import sys 21 import sys
22 22
23 import dag 23 import dag
24 import loading_trace
24 import request_dependencies_lens 25 import request_dependencies_lens
25 26
26 class ResourceGraph(object): 27 class ResourceGraph(object):
27 """A model of loading by a DAG (tree?) of resource dependancies. 28 """A model of loading by a DAG (tree?) of resource dependancies.
28 29
29 Set parameters: 30 Set parameters:
30 cache_all: if true, assume zero loading time for all resources. 31 cache_all: if true, assume zero loading time for all resources.
31 """ 32 """
32 def __init__(self, trace): 33 def __init__(self, trace):
33 """Create from a LoadingTrace. 34 """Create from a LoadingTrace (or json of a trace).
34 35
35 Args: 36 Args:
36 trace: (LoadingTrace) Loading trace. 37 trace: (LoadingTrace/JSON) Loading trace or JSON of a trace.
37 """ 38 """
39 if type(trace) == dict:
40 trace = loading_trace.LoadingTrace.FromJsonDict(trace)
38 self._BuildDag(trace) 41 self._BuildDag(trace)
39 self._global_start = min([n.StartTime() for n in self._node_info]) 42 self._global_start = min([n.StartTime() for n in self._node_info])
40 # Sort before splitting children so that we can correctly dectect if a 43 # Sort before splitting children so that we can correctly dectect if a
41 # reparented child is actually a dependency for a child of its new parent. 44 # reparented child is actually a dependency for a child of its new parent.
42 try: 45 try:
43 for n in dag.TopologicalSort(self._nodes): 46 for n in dag.TopologicalSort(self._nodes):
44 self._SplitChildrenByTime(self._node_info[n.Index()]) 47 self._SplitChildrenByTime(self._node_info[n.Index()])
45 except AssertionError as exc: 48 except AssertionError as exc:
46 sys.stderr.write('Bad topological sort: %s\n' 49 sys.stderr.write('Bad topological sort: %s\n'
47 'Skipping child split\n' % str(exc)) 50 'Skipping child split\n' % str(exc))
(...skipping 235 matching lines...) Expand 10 before | Expand all | Expand 10 after
283 for c in children: 286 for c in children:
284 assert n in c.Predecessors() # Integrity checking 287 assert n in c.Predecessors() # Integrity checking
285 queue.append(c) 288 queue.append(c)
286 assert len(visited) == len(self._nodes) 289 assert len(visited) == len(self._nodes)
287 return '\n'.join(output) 290 return '\n'.join(output)
288 291
289 ## 292 ##
290 ## Internal items 293 ## Internal items
291 ## 294 ##
292 295
293 _CONTENT_TYPE_TO_COLOR = {'html': 'red', 'css': 'green', 'script': 'blue', 296 _CONTENT_KIND_TO_COLOR = {
294 'json': 'purple', 'gif_image': 'grey', 297 'application': 'blue', # Scripts.
295 'image': 'orange', 'other': 'white'} 298 'font': 'grey70',
299 'image': 'orange', # This probably catches gifs?
300 'video': 'hotpink1',
301 }
302
303 _CONTENT_TYPE_TO_COLOR = {
304 'html': 'red',
305 'css': 'green',
306 'script': 'blue',
307 'javascript': 'blue',
308 'json': 'purple',
309 'gif': 'grey',
310 'image': 'orange',
311 'jpeg': 'orange',
312 'png': 'orange',
313 'plain': 'brown3',
314 'octet-stream': 'brown3',
315 'other': 'white',
316 }
296 317
297 # This resource type may induce a timing dependency. See _SplitChildrenByTime 318 # This resource type may induce a timing dependency. See _SplitChildrenByTime
298 # for details. 319 # for details.
299 # TODO(mattcary): are these right? 320 # TODO(mattcary): are these right?
300 _CAN_BE_TIMING_PARENT = set(['script', 'magic-debug-content']) 321 _CAN_BE_TIMING_PARENT = set(['script', 'magic-debug-content'])
301 _CAN_MAKE_TIMING_DEPENDENCE = set(['json', 'other', 'magic-debug-content']) 322 _CAN_MAKE_TIMING_DEPENDENCE = set(['json', 'other', 'magic-debug-content'])
302 323
303 class _NodeInfo(object): 324 class _NodeInfo(object):
304 """Our internal class that adds cost and other information to nodes. 325 """Our internal class that adds cost and other information to nodes.
305 326
(...skipping 10 matching lines...) Expand all
316 Args: 337 Args:
317 node: The node to augment. 338 node: The node to augment.
318 request: The request associated with this node. 339 request: The request associated with this node.
319 """ 340 """
320 self._request = request 341 self._request = request
321 self._node = node 342 self._node = node
322 self._edge_costs = {} 343 self._edge_costs = {}
323 self._edge_annotations = {} 344 self._edge_annotations = {}
324 # All fields in timing are millis relative to request_time, which is epoch 345 # All fields in timing are millis relative to request_time, which is epoch
325 # seconds. 346 # seconds.
326 self._node_cost = max([t for f, t in request.timing._asdict().iteritems() 347 self._node_cost = max(
327 if f != 'request_time']) 348 [0] + [t for f, t in request.timing._asdict().iteritems()
349 if f != 'request_time'])
328 350
329 def __str__(self): 351 def __str__(self):
330 return self.ShortName() 352 return self.ShortName()
331 353
332 def Node(self): 354 def Node(self):
333 return self._node 355 return self._node
334 356
335 def Index(self): 357 def Index(self):
336 return self._node.Index() 358 return self._node.Index()
337 359
(...skipping 18 matching lines...) Expand all
356 378
357 def ContentType(self): 379 def ContentType(self):
358 return self._request.GetContentType() 380 return self._request.GetContentType()
359 381
360 def ShortName(self): 382 def ShortName(self):
361 """Returns either the hostname of the resource, or the filename, 383 """Returns either the hostname of the resource, or the filename,
362 or the end of the path. Tries to include the domain as much as possible. 384 or the end of the path. Tries to include the domain as much as possible.
363 """ 385 """
364 parsed = urlparse.urlparse(self._request.url) 386 parsed = urlparse.urlparse(self._request.url)
365 path = parsed.path 387 path = parsed.path
388 hostname = parsed.hostname if parsed.hostname else '?.?.?'
366 if path != '' and path != '/': 389 if path != '' and path != '/':
367 last_path = parsed.path.split('/')[-1] 390 last_path = parsed.path.split('/')[-1]
368 if len(last_path) < 10: 391 if len(last_path) < 10:
369 if len(path) < 10: 392 if len(path) < 10:
370 return parsed.hostname + '/' + path 393 return hostname + '/' + path
371 else: 394 else:
372 return parsed.hostname + '/..' + parsed.path[-10:] 395 return hostname + '/..' + parsed.path[-10:]
373 elif len(last_path) > 10: 396 elif len(last_path) > 10:
374 return parsed.hostname + '/..' + last_path[:5] 397 return hostname + '/..' + last_path[:5]
375 else: 398 else:
376 return parsed.hostname + '/..' + last_path 399 return hostname + '/..' + last_path
377 else: 400 else:
378 return parsed.hostname 401 return hostname
379 402
380 def Url(self): 403 def Url(self):
381 return self._request.url 404 return self._request.url
382 405
383 def SetEdgeCost(self, child, cost): 406 def SetEdgeCost(self, child, cost):
384 assert child.Node() in self._node.Successors() 407 assert child.Node() in self._node.Successors()
385 self._edge_costs[child] = cost 408 self._edge_costs[child] = cost
386 409
387 def AddEdgeAnnotation(self, s, annotation): 410 def AddEdgeAnnotation(self, s, annotation):
388 assert s.Node() in self._node.Successors() 411 assert s.Node() in self._node.Successors()
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
456 next_index = len(self._nodes) 479 next_index = len(self._nodes)
457 assert request not in index_by_request 480 assert request not in index_by_request
458 index_by_request[request] = next_index 481 index_by_request[request] = next_index
459 node = dag.Node(next_index) 482 node = dag.Node(next_index)
460 node_info = self._NodeInfo(node, request) 483 node_info = self._NodeInfo(node, request)
461 self._nodes.append(node) 484 self._nodes.append(node)
462 self._node_info.append(node_info) 485 self._node_info.append(node_info)
463 486
464 dependencies = request_dependencies_lens.RequestDependencyLens( 487 dependencies = request_dependencies_lens.RequestDependencyLens(
465 trace).GetRequestDependencies() 488 trace).GetRequestDependencies()
466 for child_rq, parent_rq, reason in dependencies: 489 for parent_rq, child_rq, reason in dependencies:
467 parent = self._node_info[index_by_request[parent_rq]] 490 parent = self._node_info[index_by_request[parent_rq]]
468 child = self._node_info[index_by_request[child_rq]] 491 child = self._node_info[index_by_request[child_rq]]
469 edge_cost = child.StartTime() - parent.EndTime() 492 edge_cost = child.StartTime() - parent.EndTime()
470 if edge_cost < 0: 493 if edge_cost < 0:
471 edge_cost = 0 494 edge_cost = 0
472 if child.StartTime() < parent.StartTime(): 495 if child.StartTime() < parent.StartTime():
473 logging.error('Inverted dependency: %s->%s', 496 logging.error('Inverted dependency: %s->%s',
474 parent.ShortName(), child.ShortName()) 497 parent.ShortName(), child.ShortName())
475 # Note that child.StartTime() < parent.EndTime() appears to happen a 498 # Note that child.StartTime() < parent.EndTime() appears to happen a
476 # fair amount in practice. 499 # fair amount in practice.
(...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after
542 break 565 break
543 if end_mark >= len(children_by_end_time): 566 if end_mark >= len(children_by_end_time):
544 break # It's not possible to rearrange any more children. 567 break # It's not possible to rearrange any more children.
545 if go_to_next_child: 568 if go_to_next_child:
546 continue # We can't rearrange this child, but the next child may be 569 continue # We can't rearrange this child, but the next child may be
547 # eligible. 570 # eligible.
548 if children_by_end_time[end_mark].EndTime() <= current.StartTime(): 571 if children_by_end_time[end_mark].EndTime() <= current.StartTime():
549 current.ReparentTo(parent, children_by_end_time[end_mark]) 572 current.ReparentTo(parent, children_by_end_time[end_mark])
550 children_by_end_time[end_mark].AddEdgeAnnotation(current, 'timing') 573 children_by_end_time[end_mark].AddEdgeAnnotation(current, 'timing')
551 574
575 def _ContentTypeToColor(self, content_type):
576 if not content_type:
577 type_str = 'other'
578 elif '/' in content_type:
579 kind, type_str = content_type.split('/')
580 if kind in self._CONTENT_KIND_TO_COLOR:
581 return self._CONTENT_KIND_TO_COLOR[kind]
582 else:
583 type_str = content_type
584 return self._CONTENT_TYPE_TO_COLOR[type_str]
585
552 def _GraphvizNode(self, index, highlight): 586 def _GraphvizNode(self, index, highlight):
553 """Returns a graphviz node description for a given node. 587 """Returns a graphviz node description for a given node.
554 588
555 Args: 589 Args:
556 index: index of the node. 590 index: index of the node.
557 highlight: a list of node items to emphasize. Any resource url which 591 highlight: a list of node items to emphasize. Any resource url which
558 contains any highlight text will be distinguished in the output. 592 contains any highlight text will be distinguished in the output.
559 593
560 Returns: 594 Returns:
561 A string describing the resource in graphviz format. 595 A string describing the resource in graphviz format.
562 The resource is color-coded according to its content type, and its shape 596 The resource is color-coded according to its content type, and its shape
563 is oval if its max-age is less than 300s (or if it's not cacheable). 597 is oval if its max-age is less than 300s (or if it's not cacheable).
564 """ 598 """
565 node_info = self._node_info[index] 599 node_info = self._node_info[index]
566 color = self._CONTENT_TYPE_TO_COLOR[node_info.ContentType()] 600 color = self._ContentTypeToColor(node_info.ContentType())
567 max_age = node_info.Request().MaxAge() 601 max_age = node_info.Request().MaxAge()
568 shape = 'polygon' if max_age > 300 else 'oval' 602 shape = 'polygon' if max_age > 300 else 'oval'
569 styles = ['filled'] 603 styles = ['filled']
570 if highlight: 604 if highlight:
571 for fragment in highlight: 605 for fragment in highlight:
572 if fragment in node_info.Url(): 606 if fragment in node_info.Url():
573 styles.append('dotted') 607 styles.append('dotted')
574 break 608 break
575 return ('%d [label = "%s\\n%.2f->%.2f (%.2f)"; style = "%s"; ' 609 return ('%d [label = "%s\\n%.2f->%.2f (%.2f)"; style = "%s"; '
576 'fillcolor = %s; shape = %s];\n' 610 'fillcolor = %s; shape = %s];\n'
(...skipping 89 matching lines...) Expand 10 before | Expand all | Expand 10 after
666 Dict of image url + short name to NodeInfo. 700 Dict of image url + short name to NodeInfo.
667 """ 701 """
668 image_to_info = {} 702 image_to_info = {}
669 for n in self._node_info: 703 for n in self._node_info:
670 if (n.ContentType().startswith('image') and 704 if (n.ContentType().startswith('image') and
671 not self._IsAdUrl(n.Url())): 705 not self._IsAdUrl(n.Url())):
672 key = str((n.Url(), n.ShortName(), n.StartTime())) 706 key = str((n.Url(), n.ShortName(), n.StartTime()))
673 assert key not in image_to_info, n.Url() 707 assert key not in image_to_info, n.Url()
674 image_to_info[key] = n 708 image_to_info[key] = n
675 return image_to_info 709 return image_to_info
OLDNEW
« no previous file with comments | « tools/android/loading/devtools_monitor.py ('k') | tools/android/loading/loading_model_unittest.py » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698