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