blob: 2ef8c70dc563176debfc74ff03ea27c8a3dd0bfb [file] [log] [blame]
The Android Open Source Projectf6c38712009-03-03 19:28:47 -08001#!/usr/bin/env python
2
3import os
4import re
5import sys
6
7def SplitSections(buffer):
8 """Spin through the input buffer looking for section header lines.
9 When found, the name of the section is extracted. The entire contents
10 of that section is added to a result hashmap with the section name
11 as the key"""
Carl Shapirode750892010-06-08 16:37:12 -070012
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080013 # Match lines like
14 # |section_name:
15 # capturing section_name
16 headerPattern = re.compile(r'^\s+\|([a-z _]+)\:$', re.MULTILINE)
17
18 sections = {}
19 start = 0
20 anchor = -1
21 sectionName = ''
Carl Shapirode750892010-06-08 16:37:12 -070022
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080023 while True:
24 # Look for a section header
25 result = headerPattern.search(buffer, start)
Carl Shapirode750892010-06-08 16:37:12 -070026
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080027 # If there are no more, add a section from the last header to EOF
28 if result is None:
29 if anchor is not -1:
30 sections[sectionName] = buffer[anchor]
31 return sections
32
33 # Add the lines from the last header, to this one to the sections
34 # map indexed by the section name
35 if anchor is not -1:
36 sections[sectionName] = buffer[anchor:result.start()]
Carl Shapirode750892010-06-08 16:37:12 -070037
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080038 sectionName = result.group(1)
39 start = result.end()
40 anchor = start
Carl Shapirode750892010-06-08 16:37:12 -070041
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080042 return sections
43
44def FindMethods(section):
45 """Spin through the 'method code index' section and extract all
46 method signatures. When found, they are added to a result list."""
Carl Shapirode750892010-06-08 16:37:12 -070047
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080048 # Match lines like:
49 # |[abcd] com/example/app/Class.method:(args)return
50 # capturing the method signature
51 methodPattern = re.compile(r'^\s+\|\[\w{4}\] (.*)$', re.MULTILINE)
52
53 start = 0
54 methods = []
55
56 while True:
57 # Look for a method name
58 result = methodPattern.search(section, start)
Carl Shapirode750892010-06-08 16:37:12 -070059
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080060 if result is None:
61 return methods
62
63 # Add the captured signature to the method list
64 methods.append(result.group(1))
65 start = result.end()
66
67def CallsMethod(codes, method):
68 """Spin through all the input method signatures. For each one, return
69 whether or not there is method invokation line in the codes section that
70 lists the method as the target."""
Carl Shapirode750892010-06-08 16:37:12 -070071
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080072 start = 0
Carl Shapirode750892010-06-08 16:37:12 -070073
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080074 while True:
75 # Find the next reference to the method signature
76 match = codes.find(method, start)
Carl Shapirode750892010-06-08 16:37:12 -070077
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080078 if match is -1:
79 break;
Carl Shapirode750892010-06-08 16:37:12 -070080
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080081 # Find the beginning of the line the method reference is on
82 startOfLine = codes.rfind("\n", 0, match) + 1
83
84 # If the word 'invoke' comes between the beginning of the line
85 # and the method reference, then it is a call to that method rather
86 # than the beginning of the code section for that method.
87 if codes.find("invoke", startOfLine, match) is not -1:
88 return True
Carl Shapirode750892010-06-08 16:37:12 -070089
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080090 start = match + len(method)
Carl Shapirode750892010-06-08 16:37:12 -070091
The Android Open Source Projectf6c38712009-03-03 19:28:47 -080092 return False
93
94
95
96def main():
97 if len(sys.argv) is not 2 or not sys.argv[1].endswith(".jar"):
98 print "Usage:", sys.argv[0], "<filename.jar>"
99 sys.exit()
100
101 command = 'dx --dex --dump-width=1000 --dump-to=-"" "%s"' % sys.argv[1]
102
103 pipe = os.popen(command)
104
105 # Read the whole dump file into memory
106 data = pipe.read()
107 sections = SplitSections(data)
108
109 pipe.close()
110 del(data)
111
112 methods = FindMethods(sections['method code index'])
113 codes = sections['codes']
114 del(sections)
115
116 print "Dead Methods:"
117 count = 0
118
119 for method in methods:
120 if not CallsMethod(codes, method):
121 print "\t", method
122 count += 1
Carl Shapirode750892010-06-08 16:37:12 -0700123
The Android Open Source Projectf6c38712009-03-03 19:28:47 -0800124 if count is 0:
125 print "\tNone"
126
127if __name__ == '__main__':
128 main()