Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/env python
      2 #===- lib/asan/scripts/asan_symbolize.py -----------------------------------===#
      3 #
      4 #                     The LLVM Compiler Infrastructure
      5 #
      6 # This file is distributed under the University of Illinois Open Source
      7 # License. See LICENSE.TXT for details.
      8 #
      9 #===------------------------------------------------------------------------===#
     10 import glob
     11 import os
     12 import re
     13 import sys
     14 import string
     15 import subprocess
     16 
     17 pipes = {}
     18 next_inline_frameno = 0
     19 
     20 def patch_address(frameno, addr_s):
     21   ''' Subtracts 1 or 2 from the top frame's address.
     22   Top frame is normally the return address from asan_report*
     23   call, which is not expected to return at all. Because of that, this
     24   address often belongs to the next source code line, or even to a different
     25   function. '''
     26   if frameno == '0':
     27     addr = int(addr_s, 16)
     28     if os.uname()[4].startswith('arm'):
     29       # Cancel the Thumb bit
     30       addr = addr & (~1)
     31     addr -= 1
     32     return hex(addr)
     33   return addr_s
     34 
     35 def postprocess_file_name(file_name, paths_to_cut):
     36   for path_to_cut in paths_to_cut:
     37     file_name = re.sub(".*" + path_to_cut, "", file_name)
     38   file_name = re.sub(".*asan_[a-z_]*.(cc|h):[0-9]*", "[asan_rtl]", file_name)
     39   file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
     40   return file_name
     41 
     42 # TODO(glider): need some refactoring here
     43 def symbolize_addr2line(line, binary_prefix, paths_to_cut):
     44   global next_inline_frameno
     45   # Strip the log prefix ("I/asanwrapper( 1196): ").
     46   line = re.sub(r'^.*?: ', '', line)
     47   #0 0x7f6e35cf2e45  (/blah/foo.so+0x11fe45)
     48   match = re.match(r'^(\s*#)([0-9]+) *(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line, re.UNICODE)
     49   if match:
     50     frameno = match.group(2)
     51     binary = match.group(4)
     52     addr = match.group(5)
     53     addr = patch_address(frameno, addr)
     54 
     55     if binary.startswith('/'):
     56       binary = binary[1:]
     57     binary = os.path.join(binary_prefix, binary)
     58 
     59     if not os.path.exists(binary):
     60       print line.rstrip().encode('utf-8')
     61       return
     62 
     63     addr = hex(int(addr, 16))
     64 
     65     if not pipes.has_key(binary):
     66       pipes[binary] = subprocess.Popen(["addr2line", "-i", "-f", "-e", binary],
     67                          stdin=subprocess.PIPE, stdout=subprocess.PIPE)
     68     p = pipes[binary]
     69     frames = []
     70     try:
     71       print >>p.stdin, addr
     72       # This will trigger a "??" response from addr2line so we know when to stop
     73       print >>p.stdin
     74       while True:
     75         function_name = p.stdout.readline().rstrip()
     76         file_name     = p.stdout.readline().rstrip()
     77         if function_name in ['??', '']:
     78           break
     79         file_name = postprocess_file_name(file_name, paths_to_cut)
     80         frames.append((function_name, file_name))
     81     except:
     82       pass
     83     if not frames:
     84       frames.append(('', ''))
     85       # Consume another pair of "??" lines
     86       try:
     87         p.stdout.readline()
     88         p.stdout.readline()
     89       except:
     90         pass
     91     for frame in frames:
     92       inline_frameno = next_inline_frameno
     93       next_inline_frameno += 1
     94       print "%s%d" % (match.group(1).encode('utf-8'), inline_frameno), \
     95           match.group(3).encode('utf-8'), "in", frame[0], frame[1]
     96   else:
     97     print line.rstrip().encode('utf-8')
     98 
     99 
    100 binary_prefix = os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'symbols')
    101 paths_to_cut = [os.getcwd() + '/', os.environ['ANDROID_BUILD_TOP'] + '/'] + sys.argv[1:]
    102 
    103 for line in sys.stdin:
    104   line = line.decode('utf-8')
    105   symbolize_addr2line(line, binary_prefix, paths_to_cut)
    106