| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219 | 
							- #!/usr/bin/python
 
- import re
 
- import sys
 
- import copy
 
- import cPickle
 
- import os
 
- class Parser:
 
-   def __init__(self):
 
-     self.calls = {}
 
-   def enter_func(self, name):
 
-     if self.infunc and not self.extern:
 
-       self.calls.setdefault(self.infunc, set()).update( self.calledfns )
 
-     self.calledfns = set()
 
-     self.infunc = name
 
-     self.extern = False
 
-   def parse_callgraph_file(self, inp):
 
-     self.infunc = None
 
-     self.extern = False
 
-     self.calledfns = set()
 
-     for line in inp:
 
-        m = re.match(r"Call graph node for function: '([^']+)'", line)
 
-        if m:
 
-            self.enter_func(m.group(1))
 
-            continue
 
-        m = re.match(r"  CS<[^>]+> calls external node", line)
 
-        if m:
 
-            self.extern = True
 
-        m = re.match(r"  CS<[^>]+> calls function '([^']+)'", line)
 
-        if m:
 
-            self.calledfns.add(m.group(1))
 
-     self.enter_func(None)
 
-   def extract_callgraph(self):
 
-     c = self.calls
 
-     self.calls = {}
 
-     return c
 
- def transitive_closure(g):
 
-     passno = 0
 
-     changed = True
 
-     g = copy.deepcopy(g)
 
-     import random
 
-     while changed:
 
-       passno += 1
 
-       changed = False
 
-       keys = g.keys()
 
-       idx = 0
 
-       for k in keys:
 
-          idx += 1
 
-          print "Pass %d/?: %d/%d\r" %(passno, idx, len(keys)),
 
-          sys.stdout.flush()
 
-          newset = g[k].copy()
 
-          for fn in g[k]:
 
-             newset.update(g.get(fn, set()))
 
-          if len(newset) != len(g[k]):
 
-             g[k].update( newset )
 
-             changed = True
 
-       print
 
-     return g
 
- def strongly_connected_components(g):
 
-   # From https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm, done stupidly.
 
-   index_of = {}
 
-   index = [ 0 ]
 
-   lowlink = {}
 
-   S = []
 
-   onStack = set()
 
-   all_sccs = []
 
-   def strongconnect(fn):
 
-     index_of[fn] = index[0]
 
-     lowlink[fn] = index[0]
 
-     index[0] += 1
 
-     S.append(fn)
 
-     onStack.add(fn)
 
-     for w in g.get(fn, []):
 
-       if w not in index_of:
 
-         strongconnect(w)
 
-         lowlink[fn] = min(lowlink[fn], lowlink[w])
 
-       elif w in onStack:
 
-         lowlink[fn] = min(lowlink[fn], index_of[w])
 
-     if lowlink[fn] == index_of[fn]:
 
-       this_scc = []
 
-       all_sccs.append(this_scc)
 
-       while True:
 
-         w = S.pop()
 
-         onStack.remove(w)
 
-         this_scc.append(w)
 
-         if w == fn:
 
-           break
 
-   for v in g.keys():
 
-     if v not in index_of:
 
-       strongconnect(v)
 
-   return all_sccs
 
- def biggest_component(sccs):
 
-   return max(len(c) for c in sccs)
 
- def connection_bottlenecks(callgraph):
 
-   callers = {}
 
-   for fn in callgraph:
 
-     for fn2 in callgraph[fn]:
 
-       callers.setdefault(fn2, set()).add(fn)
 
-   components = strongly_connected_components(callgraph)
 
-   components.sort(key=len)
 
-   big_component_fns = components[-1]
 
-   size = len(big_component_fns)
 
-   function_bottlenecks = fn_results = []
 
-   total = len(big_component_fns)
 
-   idx = 0
 
-   for fn in big_component_fns:
 
-     idx += 1
 
-     print "Pass 1/3: %d/%d\r"%(idx, total),
 
-     sys.stdout.flush()
 
-     cg2 = copy.deepcopy(callgraph)
 
-     del cg2[fn]
 
-     fn_results.append( (size - biggest_component(strongly_connected_components(cg2)), fn) )
 
-   print
 
-   bcf_set = set(big_component_fns)
 
-   call_bottlenecks = fn_results = []
 
-   result_set = set()
 
-   total = len(big_component_fns)
 
-   idx = 0
 
-   for fn in big_component_fns:
 
-     fn_callers = callers[fn].intersection(bcf_set)
 
-     idx += 1
 
-     if len(fn_callers) != 1:
 
-       continue
 
-     print "Pass 2/3: %d/%d\r"%(idx, total),
 
-     sys.stdout.flush()
 
-     caller = fn_callers.pop()
 
-     assert len(fn_callers) == 0
 
-     cg2 = copy.deepcopy(callgraph)
 
-     cg2[caller].remove(fn)
 
-     fn_results.append( (size - biggest_component(strongly_connected_components(cg2)), fn, "called by", caller) )
 
-     result_set.add( (caller, fn) )
 
-   print
 
-   total = len(big_component_fns)
 
-   idx = 0
 
-   for fn in big_component_fns:
 
-     fn_calls = callgraph[fn].intersection(bcf_set)
 
-     idx += 1
 
-     if len(fn_calls) != 1:
 
-       continue
 
-     print "Pass 3/3: %d/%d\r"%(idx, total),
 
-     sys.stdout.flush()
 
-     callee = fn_calls.pop()
 
-     if (fn, callee) in result_set:
 
-       continue
 
-     assert len(fn_calls) == 0
 
-     cg2 = copy.deepcopy(callgraph)
 
-     cg2[fn].remove(callee)
 
-     fn_results.append( (size - biggest_component(strongly_connected_components(cg2)), callee, "called by", fn) )
 
-   print
 
-   return (function_bottlenecks, call_bottlenecks)
 
- if __name__ == '__main__':
 
-     p = Parser()
 
-     for fname in sys.argv[1:]:
 
-       with open(fname, 'r') as f:
 
-         p.parse_callgraph_file(f)
 
-     sys.stdout.flush
 
-     print "Building callgraph"
 
-     callgraph = p.extract_callgraph()
 
-     print "Finding strongly connected components"
 
-     sccs = strongly_connected_components(callgraph)
 
-     print "Finding the transitive closure of the callgraph.."
 
-     closure = transitive_closure(callgraph)
 
-     print "Finding bottlenecks..."
 
-     bottlenecks = connection_bottlenecks(callgraph)
 
-     data = {
 
-       'callgraph' : callgraph,
 
-       'sccs' : sccs,
 
-       'closure' : closure,
 
-       'bottlenecks' : bottlenecks }
 
-     with open('callgraph.pkl', 'w') as f:
 
-       cPickle.dump(data, f)
 
 
  |