1f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch#!/usr/bin/env python
2f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Copyright 2014 The Chromium Authors. All rights reserved.
3f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Use of this source code is governed by a BSD-style license that can be
4f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# found in the LICENSE file.
5f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
6f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochimport argparse, os, sys, json, subprocess, pickle, StringIO
7f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
8f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser = argparse.ArgumentParser(
9f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  description =
10f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    "Process the Blink points-to graph generated by the Blink GC plugin.")
11f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
12f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser.add_argument(
13f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  '-', dest='use_stdin', action='store_true',
14f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  help='Read JSON graph files from stdin')
15f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
16f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser.add_argument(
17f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  '-c', '--detect-cycles', action='store_true',
18f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  help='Detect cycles containing GC roots')
19f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
20f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser.add_argument(
21f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  '-s', '--print-stats', action='store_true',
22f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  help='Statistics about ref-counted and traced objects')
23f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
24f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser.add_argument(
25f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  '-v', '--verbose', action='store_true',
26f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  help='Verbose output')
27f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
28f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser.add_argument(
29f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  '--ignore-cycles', default=None, metavar='FILE',
30f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  help='File with cycles to ignore')
31f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
32f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser.add_argument(
33f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  '--ignore-classes', nargs='*', default=[], metavar='CLASS',
34f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  help='Classes to ignore when detecting cycles')
35f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
36f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser.add_argument(
37f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  '--pickle-graph', default=None, metavar='FILE',
38f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  help='File to read/save the graph from/to')
39f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
40f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochparser.add_argument(
41f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'files', metavar='FILE_OR_DIR', nargs='*', default=[],
42f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  help='JSON graph files or directories containing them')
43f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
44f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Command line args after parsing.
45f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochargs = None
46f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
47f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Map from node labels to nodes.
48f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochgraph = {}
49f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
50f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Set of root nodes.
51f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochroots = []
52f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
53f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# List of cycles to ignore.
54f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochignored_cycles = []
55f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
56f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Global flag to determine exit code.
57f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochglobal_reported_error = False
58f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
59f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef set_reported_error(value):
60f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  global global_reported_error
61f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  global_reported_error = value
62f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
63f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef reported_error():
64f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  return global_reported_error
65f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
66f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef log(msg):
67f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if args.verbose:
68f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    print msg
69f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
70f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochglobal_inc_copy = 0
71f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef inc_copy():
72f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  global global_inc_copy
73f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  global_inc_copy += 1
74f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
75f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef get_node(name):
76f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  return graph.setdefault(name, Node(name))
77f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
78f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochptr_types = ('raw', 'ref', 'mem')
79f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
80f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef inc_ptr(dst, ptr):
81f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if ptr in ptr_types:
82f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    node = graph.get(dst)
83f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if not node: return
84f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    node.counts[ptr] += 1
85f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
86f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef add_counts(s1, s2):
87f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for (k, v) in s2.iteritems():
88f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    s1[k] += s2[k]
89f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
90f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Representation of graph nodes. Basically a map of directed edges.
91f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochclass Node:
92f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def __init__(self, name):
93f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.name = name
94f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.edges = {}
95f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.reset()
96f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def __repr__(self):
97f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return "%s(%s) %s" % (self.name, self.visited, self.edges)
98f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def update_node(self, decl):
99f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # Currently we don't track any node info besides its edges.
100f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    pass
101f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def update_edge(self, e):
102f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    new_edge = Edge(**e)
103f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    edge = self.edges.get(new_edge.key)
104f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if edge:
105f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      # If an edge exist, its kind is the strongest of the two.
106f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      edge.kind = max(edge.kind, new_edge.kind)
107f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    else:
108f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      self.edges[new_edge.key] = new_edge
109f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def super_edges(self):
110f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return [ e for e in self.edges.itervalues() if e.is_super() ]
111f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def subclass_edges(self):
112f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return [ e for e in self.edges.itervalues() if e.is_subclass() ]
113f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def reset(self):
114f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.cost = sys.maxint
115f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.visited = False
116f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.path = None
117f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.counts = {}
118f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    for ptr in ptr_types:
119f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      self.counts[ptr] = 0
120f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def update_counts(self):
121f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    for e in self.edges.itervalues():
122f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      inc_ptr(e.dst, e.ptr)
123f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
124f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Representation of directed graph edges.
125f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochclass Edge:
126f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def __init__(self, **decl):
127f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.src = decl['src']
128f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.dst = decl['dst']
129f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.lbl = decl['lbl']
130f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.ptr = decl['ptr']
131f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.kind = decl['kind'] # 0 = weak, 1 = strong, 2 = root
132f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.loc = decl['loc']
133f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # The label does not uniquely determine an edge from a node. We
134f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # define the semi-unique key to be the concatenation of the
135f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # label and dst name. This is sufficient to track the strongest
136f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # edge to a particular type. For example, if the field A::m_f
137f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # has type HashMap<WeakMember<B>, Member<B>> we will have a
138f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # strong edge with key m_f#B from A to B.
139f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    self.key = '%s#%s' % (self.lbl, self.dst)
140f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def __repr__(self):
141f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return '%s (%s) => %s' % (self.src, self.lbl, self.dst)
142f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def is_root(self):
143f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return self.kind == 2
144f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def is_weak(self):
145f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return self.kind == 0
146f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def keeps_alive(self):
147f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return self.kind > 0
148f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def is_subclass(self):
149f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return self.lbl.startswith('<subclass>')
150f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  def is_super(self):
151f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return self.lbl.startswith('<super>')
152f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
153f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef parse_file(filename):
154f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  obj = json.load(open(filename))
155f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  return obj
156f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
157f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef build_graphs_in_dir(dirname):
158f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  # TODO: Use plateform independent code, eg, os.walk
159f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  files = subprocess.check_output(
160f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    ['find', dirname, '-name', '*.graph.json']).split('\n')
161f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  log("Found %d files" % len(files))
162f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for f in files:
163f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    f.strip()
164f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if len(f) < 1:
165f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      continue
166f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    build_graph(f)
167f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
168f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef build_graph(filename):
169f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for decl in parse_file(filename):
170f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if decl.has_key('name'):
171f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      # Add/update a node entry
172f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      name = decl['name']
173f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      node = get_node(name)
174f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      node.update_node(decl)
175f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    else:
176f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      # Add/update an edge entry
177f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      name = decl['src']
178f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      node = get_node(name)
179f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      node.update_edge(decl)
180f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
181f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# Copy all non-weak edges from super classes to their subclasses.
182f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# This causes all fields of a super to be considered fields of a
183f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# derived class without tranitively relating derived classes with
184f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# each other. For example, if B <: A, C <: A, and for some D, D => B,
185f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch# we don't want that to entail that D => C.
186f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef copy_super_edges(edge):
187f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if edge.is_weak() or not edge.is_super():
188f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return
189f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  inc_copy()
190f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  # Make the super-class edge weak (prohibits processing twice).
191f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  edge.kind = 0
192f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  # If the super class is not in our graph exit early.
193f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  super_node = graph.get(edge.dst)
194f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if super_node is None: return
195f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  # Recursively copy all super-class edges.
196f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for e in super_node.super_edges():
197f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    copy_super_edges(e)
198f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  # Copy strong super-class edges (ignoring sub-class edges) to the sub class.
199f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  sub_node = graph[edge.src]
200f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for e in super_node.edges.itervalues():
201f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if e.keeps_alive() and not e.is_subclass():
202f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      new_edge = Edge(
203f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        src = sub_node.name,
204f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        dst = e.dst,
205f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        lbl = '%s <: %s' % (super_node.name, e.lbl),
206f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        ptr = e.ptr,
207f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        kind = e.kind,
208f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        loc = e.loc,
209f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      )
210f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      sub_node.edges[new_edge.key] = new_edge
211f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  # Add a strong sub-class edge.
212f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  sub_edge = Edge(
213f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    src = super_node.name,
214f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    dst = sub_node.name,
215f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    lbl = '<subclass>',
216f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    ptr = edge.ptr,
217f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    kind = 1,
218f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    loc = edge.loc,
219f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  )
220f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  super_node.edges[sub_edge.key] = sub_edge
221f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
222f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef complete_graph():
223f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for node in graph.itervalues():
224f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    for edge in node.super_edges():
225f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      copy_super_edges(edge)
226f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    for edge in node.edges.itervalues():
227f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if edge.is_root():
228f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        roots.append(edge)
229f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  log("Copied edges down <super> edges for %d graph nodes" % global_inc_copy)
230f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
231f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef reset_graph():
232f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for n in graph.itervalues():
233f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    n.reset()
234f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
235f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef shortest_path(start, end):
236f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  start.cost = 0
237f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  minlist = [start]
238f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  while len(minlist) > 0:
239f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    minlist.sort(key=lambda n: -n.cost)
240f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    current = minlist.pop()
241f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    current.visited = True
242f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if current == end or current.cost >= end.cost + 1:
243f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      return
244f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    for e in current.edges.itervalues():
245f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if not e.keeps_alive():
246f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        continue
247f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      dst = graph.get(e.dst)
248f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if dst is None or dst.visited:
249f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        continue
250f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if current.cost < dst.cost:
251f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        dst.cost = current.cost + 1
252f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        dst.path = e
253f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      minlist.append(dst)
254f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
255f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef detect_cycles():
256f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for root_edge in roots:
257f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    reset_graph()
258f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # Mark ignored classes as already visited
259f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    for ignore in args.ignore_classes:
260f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      name = ignore.find("::") > 0 and ignore or ("blink::" + ignore)
261f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      node = graph.get(name)
262f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if node:
263f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        node.visited = True
264f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    src = graph[root_edge.src]
265f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    dst = graph.get(root_edge.dst)
266f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if src.visited:
267f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      continue
268f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if root_edge.dst == "WTF::String":
269f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      continue
270f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if dst is None:
271f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      print "\nPersistent root to incomplete destination object:"
272f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      print root_edge
273f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      set_reported_error(True)
274f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      continue
275f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    # Find the shortest path from the root target (dst) to its host (src)
276f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    shortest_path(dst, src)
277f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if src.cost < sys.maxint:
278f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      report_cycle(root_edge)
279f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
280f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef is_ignored_cycle(cycle):
281f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for block in ignored_cycles:
282f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if block_match(cycle, block):
283f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      return True
284f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
285f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef block_match(b1, b2):
286f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if len(b1) != len(b2):
287f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return False
288f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for (l1, l2) in zip(b1, b2):
289f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if l1 != l2:
290f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      return False
291f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  return True
292f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
293f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef report_cycle(root_edge):
294f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  dst = graph[root_edge.dst]
295f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  path = []
296f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  edge = root_edge
297f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  dst.path = None
298f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  while edge:
299f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    path.append(edge)
300f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    edge = graph[edge.src].path
301f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  path.append(root_edge)
302f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  path.reverse()
303f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  # Find the max loc length for pretty printing.
304f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  max_loc = 0
305f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for p in path:
306f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if len(p.loc) > max_loc:
307f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      max_loc = len(p.loc)
308f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  out = StringIO.StringIO()
309f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for p in path[:-1]:
310f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    print >>out, (p.loc + ':').ljust(max_loc + 1), p
311f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  sout = out.getvalue()
312f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if not is_ignored_cycle(sout):
313f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    print "\nFound a potentially leaking cycle starting from a GC root:\n", sout
314f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    set_reported_error(True)
315f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
316f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef load_graph():
317f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  global graph
318f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  global roots
319f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  log("Reading graph from pickled file: " + args.pickle_graph)
320f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  dump = pickle.load(open(args.pickle_graph, 'rb'))
321f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  graph = dump[0]
322f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  roots = dump[1]
323f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
324f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef save_graph():
325f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  log("Saving graph to pickle file: " + args.pickle_graph)
326f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  dump = (graph, roots)
327f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  pickle.dump(dump, open(args.pickle_graph, 'wb'))
328f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
329f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef read_ignored_cycles():
330f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  global ignored_cycles
331f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if not args.ignore_cycles:
332f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return
333f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  log("Reading ignored cycles from file: " + args.ignore_cycles)
334f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  block = []
335f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for l in open(args.ignore_cycles):
336f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    line = l.strip()
337f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if not line or line.startswith('Found'):
338f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if len(block) > 0:
339f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        ignored_cycles.append(block)
340f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      block = []
341f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    else:
342f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      block += l
343f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if len(block) > 0:
344f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    ignored_cycles.append(block)
345f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
346f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochgc_bases = (
347f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'blink::GarbageCollected',
348f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'blink::GarbageCollectedFinalized',
349f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'blink::GarbageCollectedMixin',
350f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch)
351f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochref_bases = (
352f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'WTF::RefCounted',
353f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'WTF::ThreadSafeRefCounted',
354f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch)
355f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochgcref_bases = (
356f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'blink::RefCountedGarbageCollected',
357f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'blink::ThreadSafeRefCountedGarbageCollected',
358f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch)
359f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochref_mixins = (
360f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'blink::EventTarget',
361f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'blink::EventTargetWithInlineData',
362f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  'blink::ActiveDOMObject',
363f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch)
364f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
365f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef print_stats():
366f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  gcref_managed = []
367f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  ref_managed = []
368f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  gc_managed = []
369f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  hierarchies = []
370f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
371f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for node in graph.itervalues():
372f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    node.update_counts()
373f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    for sup in node.super_edges():
374f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if sup.dst in gcref_bases:
375f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        gcref_managed.append(node)
376f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      elif sup.dst in ref_bases:
377f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        ref_managed.append(node)
378f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      elif sup.dst in gc_bases:
379f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        gc_managed.append(node)
380f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
381f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  groups = [("GC manged   ", gc_managed),
382f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch            ("ref counted ", ref_managed),
383f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch            ("in transition", gcref_managed)]
384f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  total = sum([len(g) for (s,g) in groups])
385f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for (s, g) in groups:
386f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    percent = len(g) * 100 / total
387f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    print "%2d%% is %s (%d hierarchies)" % (percent, s, len(g))
388f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
389f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for base in gcref_managed:
390f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    stats = dict({ 'classes': 0, 'ref-mixins': 0 })
391f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    for ptr in ptr_types: stats[ptr] = 0
392f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    hierarchy_stats(base, stats)
393f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    hierarchies.append((base, stats))
394f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
395f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  print "\nHierarchies in transition (RefCountedGarbageCollected):"
396f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  hierarchies.sort(key=lambda (n,s): -s['classes'])
397f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for (node, stats) in hierarchies:
398f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    total = stats['mem'] + stats['ref'] + stats['raw']
399f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    print (
400f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      "%s %3d%% of %-30s: %3d cls, %3d mem, %3d ref, %3d raw, %3d ref-mixins" %
401f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      (stats['ref'] == 0 and stats['ref-mixins'] == 0 and "*" or " ",
402f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch       total == 0 and 100 or stats['mem'] * 100 / total,
403f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch       node.name.replace('blink::', ''),
404f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch       stats['classes'],
405f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch       stats['mem'],
406f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch       stats['ref'],
407f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch       stats['raw'],
408f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch       stats['ref-mixins'],
409f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch     ))
410f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
411f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef hierarchy_stats(node, stats):
412f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if not node: return
413f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  stats['classes'] += 1
414f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  add_counts(stats, node.counts)
415f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for edge in node.super_edges():
416f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if edge.dst in ref_mixins:
417f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      stats['ref-mixins'] += 1
418f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  for edge in node.subclass_edges():
419f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    hierarchy_stats(graph.get(edge.dst), stats)
420f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
421f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochdef main():
422f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  global args
423f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  args = parser.parse_args()
424f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if not (args.detect_cycles or args.print_stats):
425f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    print "Please select an operation to perform (eg, -c to detect cycles)"
426f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    parser.print_help()
427f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return 1
428f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if args.pickle_graph and os.path.isfile(args.pickle_graph):
429f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    load_graph()
430f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  else:
431f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if args.use_stdin:
432f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      log("Reading files from stdin")
433f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      for f in sys.stdin:
434f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        build_graph(f.strip())
435f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    else:
436f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      log("Reading files and directories from command line")
437f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      if len(args.files) == 0:
438f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        print "Please provide files or directores for building the graph"
439f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        parser.print_help()
440f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        return 1
441f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      for f in args.files:
442f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        if os.path.isdir(f):
443f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch          log("Building graph from files in directory: " + f)
444f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch          build_graphs_in_dir(f)
445f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch        else:
446f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch          log("Building graph from file: " + f)
447f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch          build_graph(f)
448f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    log("Completing graph construction (%d graph nodes)" % len(graph))
449f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    complete_graph()
450f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    if args.pickle_graph:
451f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch      save_graph()
452f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if args.detect_cycles:
453f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    read_ignored_cycles()
454f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    log("Detecting cycles containg GC roots")
455f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    detect_cycles()
456f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if args.print_stats:
457f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    log("Printing statistics")
458f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    print_stats()
459f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  if reported_error():
460f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch    return 1
461f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  return 0
462f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch
463f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdochif __name__ == '__main__':
464f91f0611dbaf29ca0f1d4aecb357ce243a19d2faBen Murdoch  sys.exit(main())
465