OLD | NEW |
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. |
(...skipping 12 matching lines...) Expand all Loading... |
23 import dag | 23 import dag |
24 import loading_trace | 24 import loading_trace |
25 import request_dependencies_lens | 25 import request_dependencies_lens |
26 | 26 |
27 class ResourceGraph(object): | 27 class ResourceGraph(object): |
28 """A model of loading by a DAG (tree?) of resource dependancies. | 28 """A model of loading by a DAG (tree?) of resource dependancies. |
29 | 29 |
30 Set parameters: | 30 Set parameters: |
31 cache_all: if true, assume zero loading time for all resources. | 31 cache_all: if true, assume zero loading time for all resources. |
32 """ | 32 """ |
33 def __init__(self, trace): | 33 def __init__(self, trace, content_lens=None): |
34 """Create from a LoadingTrace (or json of a trace). | 34 """Create from a LoadingTrace (or json of a trace). |
35 | 35 |
36 Args: | 36 Args: |
37 trace: (LoadingTrace/JSON) Loading trace or JSON of a trace. | 37 trace: (LoadingTrace/JSON) Loading trace or JSON of a trace. |
| 38 content_lens: (ContentClassificationLens) Lens used to annotate the |
| 39 nodes, or None. |
38 """ | 40 """ |
39 if type(trace) == dict: | 41 if type(trace) == dict: |
40 trace = loading_trace.LoadingTrace.FromJsonDict(trace) | 42 trace = loading_trace.LoadingTrace.FromJsonDict(trace) |
| 43 self._content_lens = content_lens |
41 self._BuildDag(trace) | 44 self._BuildDag(trace) |
42 self._global_start = min([n.StartTime() for n in self._node_info]) | 45 self._global_start = min([n.StartTime() for n in self._node_info]) |
43 # Sort before splitting children so that we can correctly dectect if a | 46 # Sort before splitting children so that we can correctly dectect if a |
44 # reparented child is actually a dependency for a child of its new parent. | 47 # reparented child is actually a dependency for a child of its new parent. |
45 try: | 48 try: |
46 for n in dag.TopologicalSort(self._nodes): | 49 for n in dag.TopologicalSort(self._nodes): |
47 self._SplitChildrenByTime(self._node_info[n.Index()]) | 50 self._SplitChildrenByTime(self._node_info[n.Index()]) |
48 except AssertionError as exc: | 51 except AssertionError as exc: |
49 sys.stderr.write('Bad topological sort: %s\n' | 52 sys.stderr.write('Bad topological sort: %s\n' |
50 'Skipping child split\n' % str(exc)) | 53 'Skipping child split\n' % str(exc)) |
(...skipping 281 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
332 information like content type. | 335 information like content type. |
333 """ | 336 """ |
334 def __init__(self, node, request): | 337 def __init__(self, node, request): |
335 """Create a new node info. | 338 """Create a new node info. |
336 | 339 |
337 Args: | 340 Args: |
338 node: The node to augment. | 341 node: The node to augment. |
339 request: The request associated with this node. | 342 request: The request associated with this node. |
340 """ | 343 """ |
341 self._request = request | 344 self._request = request |
| 345 self._is_ad = False |
| 346 self._is_tracking = False |
342 self._node = node | 347 self._node = node |
343 self._edge_costs = {} | 348 self._edge_costs = {} |
344 self._edge_annotations = {} | 349 self._edge_annotations = {} |
345 # All fields in timing are millis relative to request_time, which is epoch | 350 # All fields in timing are millis relative to request_time, which is epoch |
346 # seconds. | 351 # seconds. |
347 self._node_cost = max( | 352 self._node_cost = max( |
348 [0] + [t for f, t in request.timing._asdict().iteritems() | 353 [0] + [t for f, t in request.timing._asdict().iteritems() |
349 if f != 'request_time']) | 354 if f != 'request_time']) |
350 | 355 |
351 def __str__(self): | 356 def __str__(self): |
352 return self.ShortName() | 357 return self.ShortName() |
353 | 358 |
354 def Node(self): | 359 def Node(self): |
355 return self._node | 360 return self._node |
356 | 361 |
357 def Index(self): | 362 def Index(self): |
358 return self._node.Index() | 363 return self._node.Index() |
359 | 364 |
| 365 def SetRequestContent(self, is_ad, is_tracking): |
| 366 """Sets the kind of content the request relates to. |
| 367 |
| 368 Args: |
| 369 is_ad: (bool) Whether the request is an Ad. |
| 370 is_tracking: (bool) Whether the request is related to tracking. |
| 371 """ |
| 372 (self._is_ad, self._is_tracking) = (is_ad, is_tracking) |
| 373 |
| 374 def IsAd(self): |
| 375 return self._is_ad |
| 376 |
| 377 def IsTracking(self): |
| 378 return self._is_tracking |
| 379 |
360 def Request(self): | 380 def Request(self): |
361 return self._request | 381 return self._request |
362 | 382 |
363 def NodeCost(self): | 383 def NodeCost(self): |
364 return self._node_cost | 384 return self._node_cost |
365 | 385 |
366 def EdgeCost(self, s): | 386 def EdgeCost(self, s): |
367 return self._edge_costs[s] | 387 return self._edge_costs[s] |
368 | 388 |
369 def StartTime(self): | 389 def StartTime(self): |
(...skipping 104 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
474 """ | 494 """ |
475 self._nodes = [] | 495 self._nodes = [] |
476 self._node_info = [] | 496 self._node_info = [] |
477 index_by_request = {} | 497 index_by_request = {} |
478 for request in trace.request_track.GetEvents(): | 498 for request in trace.request_track.GetEvents(): |
479 next_index = len(self._nodes) | 499 next_index = len(self._nodes) |
480 assert request not in index_by_request | 500 assert request not in index_by_request |
481 index_by_request[request] = next_index | 501 index_by_request[request] = next_index |
482 node = dag.Node(next_index) | 502 node = dag.Node(next_index) |
483 node_info = self._NodeInfo(node, request) | 503 node_info = self._NodeInfo(node, request) |
| 504 if self._content_lens: |
| 505 node.SetRequestContent(self._content_lens.IsAdRequest(request), |
| 506 self._content_lens.IsTrackingRequest(request)) |
484 self._nodes.append(node) | 507 self._nodes.append(node) |
485 self._node_info.append(node_info) | 508 self._node_info.append(node_info) |
486 | 509 |
487 dependencies = request_dependencies_lens.RequestDependencyLens( | 510 dependencies = request_dependencies_lens.RequestDependencyLens( |
488 trace).GetRequestDependencies() | 511 trace).GetRequestDependencies() |
489 for parent_rq, child_rq, reason in dependencies: | 512 for parent_rq, child_rq, reason in dependencies: |
490 parent = self._node_info[index_by_request[parent_rq]] | 513 parent = self._node_info[index_by_request[parent_rq]] |
491 child = self._node_info[index_by_request[child_rq]] | 514 child = self._node_info[index_by_request[child_rq]] |
492 edge_cost = child.StartTime() - parent.EndTime() | 515 edge_cost = child.StartTime() - parent.EndTime() |
493 if edge_cost < 0: | 516 if edge_cost < 0: |
(...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
599 node_info = self._node_info[index] | 622 node_info = self._node_info[index] |
600 color = self._ContentTypeToColor(node_info.ContentType()) | 623 color = self._ContentTypeToColor(node_info.ContentType()) |
601 max_age = node_info.Request().MaxAge() | 624 max_age = node_info.Request().MaxAge() |
602 shape = 'polygon' if max_age > 300 else 'oval' | 625 shape = 'polygon' if max_age > 300 else 'oval' |
603 styles = ['filled'] | 626 styles = ['filled'] |
604 if highlight: | 627 if highlight: |
605 for fragment in highlight: | 628 for fragment in highlight: |
606 if fragment in node_info.Url(): | 629 if fragment in node_info.Url(): |
607 styles.append('dotted') | 630 styles.append('dotted') |
608 break | 631 break |
| 632 if node_info.IsAd() or node_info.IsTracking(): |
| 633 styles += ['bold', 'diagonals'] |
609 return ('%d [label = "%s\\n%.2f->%.2f (%.2f)"; style = "%s"; ' | 634 return ('%d [label = "%s\\n%.2f->%.2f (%.2f)"; style = "%s"; ' |
610 'fillcolor = %s; shape = %s];\n' | 635 'fillcolor = %s; shape = %s];\n' |
611 % (index, node_info.ShortName(), | 636 % (index, node_info.ShortName(), |
612 node_info.StartTime() - self._global_start, | 637 node_info.StartTime() - self._global_start, |
613 node_info.EndTime() - self._global_start, | 638 node_info.EndTime() - self._global_start, |
614 node_info.EndTime() - node_info.StartTime(), | 639 node_info.EndTime() - node_info.StartTime(), |
615 ','.join(styles), color, shape)) | 640 ','.join(styles), color, shape)) |
616 | 641 |
617 @classmethod | 642 @classmethod |
618 def _IsAdUrl(cls, url): | 643 def _IsAdUrl(cls, url): |
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
700 Dict of image url + short name to NodeInfo. | 725 Dict of image url + short name to NodeInfo. |
701 """ | 726 """ |
702 image_to_info = {} | 727 image_to_info = {} |
703 for n in self._node_info: | 728 for n in self._node_info: |
704 if (n.ContentType().startswith('image') and | 729 if (n.ContentType().startswith('image') and |
705 not self._IsAdUrl(n.Url())): | 730 not self._IsAdUrl(n.Url())): |
706 key = str((n.Url(), n.ShortName(), n.StartTime())) | 731 key = str((n.Url(), n.ShortName(), n.StartTime())) |
707 assert key not in image_to_info, n.Url() | 732 assert key not in image_to_info, n.Url() |
708 image_to_info[key] = n | 733 image_to_info[key] = n |
709 return image_to_info | 734 return image_to_info |
OLD | NEW |