Home | History | Annotate | Download | only in tools
      1 #!/usr/bin/env python
      2 
      3 import os
      4 import re
      5 import sys
      6 
      7 def 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"""
     12 
     13     # 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 = ''
     22 
     23     while True:
     24         # Look for a section header
     25         result = headerPattern.search(buffer, start)
     26 
     27         # 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()]
     37 
     38         sectionName = result.group(1)
     39         start = result.end()
     40         anchor = start
     41 
     42     return sections
     43 
     44 def 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."""
     47 
     48     # 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)
     59 
     60         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 
     67 def 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."""
     71 
     72     start = 0
     73 
     74     while True:
     75         # Find the next reference to the method signature
     76         match = codes.find(method, start)
     77 
     78         if match is -1:
     79             break;
     80 
     81         # 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
     89 
     90         start = match + len(method)
     91 
     92     return False
     93 
     94 
     95 
     96 def 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
    123 
    124     if count is 0:
    125         print "\tNone"
    126 
    127 if __name__ == '__main__':
    128     main()
    129