mdd.py 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. #!/usr/bin/env python2.3
  2. import re, sys
  3. import textwrap
  4. files = sys.argv[1:]
  5. funcDeclaredIn = {}
  6. fileDeclares = {}
  7. functionCalls = {}
  8. funcCalledByFile = {}
  9. funcCalledByFunc = {}
  10. cpp_re = re.compile(r'//.*$')
  11. c_re = re.compile(r'/[*]+(?:[^*]+|[*]+[^/*])*[*]+/', re.M|re.S)
  12. for fname in files:
  13. f = open(fname, 'r')
  14. curFunc = "???"
  15. functionCalls.setdefault(curFunc,{})
  16. lineno = 0
  17. body = f.read()
  18. body = cpp_re.sub(" ",body)
  19. body = c_re.sub(" ",body)
  20. #if fname == 'dns.c': print body
  21. for line in body.split("\n"):
  22. lineno += 1
  23. m = re.match(r'^[^\s/].*\s(\w+)\([^;]*$', line)
  24. if m:
  25. #print line, "->", m.group(1)
  26. curFunc = m.group(1)
  27. if curFunc[0] == '_': curFunc = curFunc[1:]
  28. functionCalls.setdefault(curFunc,{})
  29. funcDeclaredIn[m.group(1)] = fname
  30. fileDeclares.setdefault(fname, {})[m.group(1)] = 1
  31. continue
  32. m = re.match(r'^(\w+)\([^;]', line)
  33. if m:
  34. #print line, "->", m.group(1)
  35. curFunc = m.group(1)
  36. if curFunc[0] == '_': curFunc = curFunc[1:]
  37. functionCalls.setdefault(curFunc,{})
  38. funcDeclaredIn[m.group(1)] = fname
  39. fileDeclares.setdefault(fname, {})[m.group(1)] = 1
  40. continue
  41. while line:
  42. m = re.search(r'(\w+)\(', line)
  43. if not m: break
  44. #print fname, line, curFunc, "->", m.group(1)
  45. fn = m.group(1)
  46. if fn[0] == '_':
  47. fn = fn[1:]
  48. functionCalls[curFunc][m.group(1)] = 1
  49. #if curFunc == "???":
  50. # print ">>!!!!! at %s:%s"%(fname,lineno)
  51. funcCalledByFunc.setdefault(m.group(1), {})[curFunc]=1
  52. funcCalledByFile.setdefault(m.group(1), {})[fname]=1
  53. line = line[m.end():]
  54. f.close()
  55. fileUsers = {}
  56. fileUses = {}
  57. for fname in files:
  58. print "%s:"%fname
  59. users = {}
  60. for func in fileDeclares[fname]:
  61. cb = funcCalledByFile.get(func,{}).keys()
  62. for f in cb: users[f] = 1
  63. #print "users[%s] = %s"%(f,users[f])
  64. users = users.keys()
  65. users.sort()
  66. fileUsers[fname] = users
  67. for user in users:
  68. fileUses.setdefault(user,[]).append(fname)
  69. if user == fname: continue
  70. print " from %s:"%user
  71. for func in fileDeclares[fname]:
  72. if funcCalledByFile.get(func,{}).get(user,0):
  73. print " %s()"%func
  74. def wrap(s, pre):
  75. return textwrap.fill(s,
  76. width=77, initial_indent=pre,
  77. subsequent_indent=" "*len(pre))
  78. for fname in files:
  79. print
  80. print "===== %s"%fname
  81. print wrap(" ".join(fileUses[fname]),
  82. " Calls: ")
  83. print wrap(" ".join(fileUsers[fname]),
  84. " Called by: ")
  85. print "=============================="
  86. funcnames = functionCalls.keys()
  87. funcnames.sort()
  88. if 1:
  89. for func in funcnames:
  90. print "===== %s"%func
  91. callers = [c for c in funcCalledByFunc.get(func,{}).keys()
  92. if c != "???"]
  93. callers.sort()
  94. called = [c for c in functionCalls[func].keys() if c != "???" and
  95. c in funcnames]
  96. called.sort()
  97. print wrap(" ".join(callers),
  98. " Called by:")
  99. print wrap(" ".join(called),
  100. " Calls:")
  101. # simple topological sort.
  102. functionDepth = {}
  103. while 1:
  104. BIG = 1000000
  105. any = 0
  106. for func in funcnames:
  107. if functionDepth.has_key(func):
  108. continue
  109. called = [c for c in functionCalls[func] if c != func and
  110. functionCalls.has_key(c)]
  111. if len(called) == 0:
  112. functionDepth[func] = 0
  113. #print "Depth(%s)=%s"%(func,0)
  114. any = 1
  115. continue
  116. calledDepths = [ functionDepth.get(c,BIG) for c in called ]
  117. if max(calledDepths) < BIG:
  118. d = functionDepth[func] = max(calledDepths)+1
  119. #print "Depth(%s)=%s"%(func,d)
  120. any = 1
  121. continue
  122. if not any:
  123. break
  124. # compute lexical closure.
  125. cycCalls = {}
  126. for func in funcnames:
  127. if not functionDepth.has_key(func):
  128. calls = [ c for c in functionCalls[func] if c != func and
  129. functionCalls.has_key(c) and not functionDepth.has_key(c)]
  130. cycCalls[func] = d = {}
  131. for c in calls:
  132. d[c]=1
  133. cycNames = cycCalls.keys()
  134. while 1:
  135. any = 0
  136. for func in cycNames:
  137. L = len(cycCalls[func])
  138. for called in cycCalls[func].keys():
  139. cycCalls[func].update(cycCalls[called])
  140. if L != len(cycCalls[func]):
  141. any = 1
  142. if not any:
  143. break
  144. depthList = [ (v,k) for k,v in functionDepth.items() ]
  145. depthList.sort()
  146. cycList = [ (len(v),k) for k,v in cycCalls.items() ]
  147. cycList.sort()
  148. for depth,name in depthList:
  149. print "Depth[%s]=%s"%(name,depth)
  150. for bredth,name in cycList:
  151. print "Width[%s]=%s"%(name,bredth)
  152. print "Sorted %s / %s"%(len(functionDepth),len(funcnames))