analyze_callgraph.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117
  1. #!/usr/bin/python
  2. import re
  3. import sys
  4. import copy
  5. import cPickle
  6. import os
  7. class Parser:
  8. def __init__(self):
  9. self.calls = {}
  10. def enter_func(self, name):
  11. if self.infunc and not self.extern:
  12. self.calls.setdefault(self.infunc, set()).update( self.calledfns )
  13. self.calledfns = set()
  14. self.infunc = name
  15. self.extern = False
  16. def parse_callgraph_file(self, inp):
  17. self.infunc = None
  18. self.extern = False
  19. self.calledfns = set()
  20. for line in inp:
  21. m = re.match(r"Call graph node for function: '([^']+)'", line)
  22. if m:
  23. self.enter_func(m.group(1))
  24. continue
  25. m = re.match(r" CS<[^>]+> calls external node", line)
  26. if m:
  27. self.extern = True
  28. m = re.match(r" CS<[^>]+> calls function '([^']+)'", line)
  29. if m:
  30. self.calledfns.add(m.group(1))
  31. self.enter_func(None)
  32. def extract_callgraph(self):
  33. c = self.calls
  34. self.calls = {}
  35. return c
  36. def transitive_closure(g):
  37. changed = True
  38. g = copy.deepcopy(g)
  39. while changed:
  40. changed = False
  41. print "X"
  42. for k in g.keys():
  43. newset = g[k].copy()
  44. for fn in g[k]:
  45. newset.update(g.get(fn, set()))
  46. if len(newset) != len(g[k]):
  47. g[k].update( newset )
  48. changed = True
  49. return g
  50. def strongly_connected_components(g):
  51. # From https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm, done stupidly.
  52. index_of = {}
  53. index = [ 0 ]
  54. lowlink = {}
  55. S = []
  56. onStack = set()
  57. all_sccs = []
  58. def strongconnect(fn):
  59. index_of[fn] = index[0]
  60. lowlink[fn] = index[0]
  61. index[0] += 1
  62. S.append(fn)
  63. onStack.add(fn)
  64. for w in g.get(fn, []):
  65. if w not in index_of:
  66. strongconnect(w)
  67. lowlink[fn] = min(lowlink[fn], lowlink[w])
  68. elif w in onStack:
  69. lowlink[fn] = min(lowlink[fn], index_of[w])
  70. if lowlink[fn] == index_of[fn]:
  71. this_scc = []
  72. all_sccs.append(this_scc)
  73. while True:
  74. w = S.pop()
  75. onStack.remove(w)
  76. this_scc.append(w)
  77. if w == fn:
  78. break
  79. for v in g.keys():
  80. if v not in index_of:
  81. strongconnect(v)
  82. return all_sccs
  83. if __name__ == '__main__':
  84. p = Parser()
  85. for fname in sys.argv[1:]:
  86. with open(fname, 'r') as f:
  87. p.parse_callgraph_file(f)
  88. callgraph = p.extract_callgraph()
  89. sccs = strongly_connected_components(callgraph)
  90. closure = transitive_closure(callgraph)
  91. with open('callgraph.pkl', 'w') as f:
  92. cPickle.dump(callgraph, f)
  93. with open('callgraph_closure.pkl', 'w') as f:
  94. cPickle.dump(closure, f)
  95. with open('callgraph_sccs.pkl', 'w') as f:
  96. cPickle.dump(sccs, f)