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