| OLD | NEW |
| (Empty) |
| 1 # Copyright 2016 The Chromium Authors. All rights reserved. | |
| 2 # Use of this source code is governed by a BSD-style license that can be | |
| 3 # found in the LICENSE file. | |
| 4 | |
| 5 """Utilities for displaying a ResourceSack. | |
| 6 | |
| 7 When run standalone, takes traces on the command line and produces a dot file to | |
| 8 stdout. | |
| 9 """ | |
| 10 | |
| 11 | |
| 12 def ToDot(sack, output, prune=-1, long_edge_msec=2000): | |
| 13 """Output as a dot file. | |
| 14 | |
| 15 Args: | |
| 16 sack: (ResourceSack) the sack to convert to dot. | |
| 17 output: a file-like output stream. | |
| 18 prune: if positive, prune & coalesce nodes under the specified threshold | |
| 19 of repeated views, as fraction node views / total graphs. All pruned | |
| 20 nodes are represented by a single node, and an edge is connected only if | |
| 21 the view count is greater than 1. | |
| 22 long_edge_msec: if positive, the definition of a long edge. Long edges are | |
| 23 distinguished in graph. | |
| 24 """ | |
| 25 output.write("""digraph dependencies { | |
| 26 rankdir = LR; | |
| 27 """) | |
| 28 | |
| 29 pruned = set() | |
| 30 num_graphs = len(sack.graph_info) | |
| 31 for bag in sack.bags: | |
| 32 if prune > 0 and float(len(bag.graphs)) / num_graphs < prune: | |
| 33 pruned.add(bag) | |
| 34 continue | |
| 35 output.write('%d [label="%s (%d)\n(%d, %d)\n(%.2f, %.2f)" shape=%s; ' | |
| 36 'style=filled; fillcolor=%s];\n' % ( | |
| 37 bag.Index(), bag.label, len(bag.graphs), | |
| 38 min(bag.total_costs), max(bag.total_costs), | |
| 39 min(bag.relative_costs), max(bag.relative_costs), | |
| 40 _CriticalToShape(bag), | |
| 41 _AmountToNodeColor(len(bag.graphs), num_graphs))) | |
| 42 | |
| 43 if pruned: | |
| 44 pruned_index = num_graphs | |
| 45 output.write('%d [label="Pruned at %.0f%%\n(%d)"; ' | |
| 46 'shape=polygon; style=dotted];\n' % | |
| 47 (pruned_index, 100 * prune, len(pruned))) | |
| 48 | |
| 49 for bag in sack.bags: | |
| 50 if bag in pruned: | |
| 51 for succ in bag.Successors(): | |
| 52 if succ not in pruned: | |
| 53 output.write('%d -> %d [style=dashed];\n' % ( | |
| 54 pruned_index, succ.Index())) | |
| 55 for succ in bag.Successors(): | |
| 56 if succ in pruned: | |
| 57 if len(bag.successor_sources[succ]) > 1: | |
| 58 output.write('%d -> %d [label="%d"; style=dashed];\n' % ( | |
| 59 bag.Index(), pruned_index, len(bag.successor_sources[succ]))) | |
| 60 else: | |
| 61 num_succ = len(bag.successor_sources[succ]) | |
| 62 num_long = 0 | |
| 63 for graph, source, target in bag.successor_sources[succ]: | |
| 64 if graph.EdgeCost(source, target) > long_edge_msec: | |
| 65 num_long += 1 | |
| 66 if num_long > 0: | |
| 67 long_frac = float(num_long) / num_succ | |
| 68 long_edge_style = '; penwidth=%f' % (2 + 6.0 * long_frac) | |
| 69 if long_frac < 0.75: | |
| 70 long_edge_style += '; style=dashed' | |
| 71 else: | |
| 72 long_edge_style = '' | |
| 73 min_edge = min(bag.successor_edge_costs[succ]) | |
| 74 max_edge = max(bag.successor_edge_costs[succ]) | |
| 75 output.write('%d -> %d [label="%d\n(%f,%f)"; color=%s %s];\n' % ( | |
| 76 bag.Index(), succ.Index(), num_succ, min_edge, max_edge, | |
| 77 _AmountToEdgeColor(num_succ, len(bag.graphs)), | |
| 78 long_edge_style)) | |
| 79 | |
| 80 output.write('}') | |
| 81 | |
| 82 | |
| 83 def _CriticalToShape(bag): | |
| 84 frac = float(bag.num_critical) / bag.num_nodes | |
| 85 if frac < 0.4: | |
| 86 return 'oval' | |
| 87 elif frac < 0.7: | |
| 88 return 'polygon' | |
| 89 elif frac < 0.9: | |
| 90 return 'trapezium' | |
| 91 return 'box' | |
| 92 | |
| 93 | |
| 94 def _AmountToNodeColor(numer, denom): | |
| 95 if denom <= 0: | |
| 96 return 'grey72' | |
| 97 ratio = 1.0 * numer / denom | |
| 98 if ratio < .3: | |
| 99 return 'white' | |
| 100 elif ratio < .6: | |
| 101 return 'yellow' | |
| 102 elif ratio < .8: | |
| 103 return 'orange' | |
| 104 return 'green' | |
| 105 | |
| 106 | |
| 107 def _AmountToEdgeColor(numer, denom): | |
| 108 color = _AmountToNodeColor(numer, denom) | |
| 109 if color == 'white' or color == 'grey72': | |
| 110 return 'black' | |
| 111 return color | |
| 112 | |
| 113 | |
| 114 def _Main(): | |
| 115 import json | |
| 116 import logging | |
| 117 import sys | |
| 118 | |
| 119 import loading_model | |
| 120 import loading_trace | |
| 121 import resource_sack | |
| 122 | |
| 123 sack = resource_sack.GraphSack() | |
| 124 for fname in sys.argv[1:]: | |
| 125 trace = loading_trace.LoadingTrace.FromJsonDict( | |
| 126 json.load(open(fname))) | |
| 127 logging.info('Making graph from %s', fname) | |
| 128 model = loading_model.ResourceGraph(trace, content_lens=None) | |
| 129 sack.ConsumeGraph(model) | |
| 130 logging.info('Finished %s', fname) | |
| 131 ToDot(sack, sys.stdout, prune=.1) | |
| 132 | |
| 133 | |
| 134 if __name__ == '__main__': | |
| 135 _Main() | |
| OLD | NEW |