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 load_addresses = {}
     19 next_inline_frameno = 0
     20 
     21 def patch_address(frameno, addr_s):
     22   ''' Subtracts 1 or 2 from the top frame's address.
     23   Top frame is normally the return address from asan_report*
     24   call, which is not expected to return at all. Because of that, this
     25   address often belongs to the next source code line, or even to a different
     26   function. '''
     27   if frameno == '0':
     28     addr = int(addr_s, 16)
     29     if os.uname()[4].startswith('arm'):
     30       # Cancel the Thumb bit
     31       addr = addr & (~1)
     32     addr -= 1
     33     return hex(addr)
     34   return addr_s
     35 
     36 def android_get_load_address(path):
     37   if load_addresses.has_key(path):
     38     return load_addresses[path]
     39   readelf_glob = os.path.join(os.environ['ANDROID_TOOLCHAIN'], '*-readelf')
     40   readelf = glob.glob(readelf_glob)[0]
     41   readelf_pipe = subprocess.Popen([readelf, "-l", path], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
     42   for line in readelf_pipe.stdout:
     43       if ('LOAD' in line) and (' E ' in line):
     44           match = re.match(r'\s*LOAD\s+0x[01-9a-zA-Z]+\s+(0x[01-9a-zA-Z]+)', line, re.UNICODE)
     45           if match:
     46               load_addr = int(match.group(1), 16)
     47               load_addresses[path] = load_addr
     48               return load_addr
     49           else: break
     50   print 'Could not make sense of readelf output!'
     51   sys.exit(1)
     52 
     53 def postprocess_file_name(file_name, paths_to_cut):
     54   for path_to_cut in paths_to_cut:
     55     file_name = re.sub(".*" + path_to_cut, "", file_name)
     56   file_name = re.sub(".*asan_[a-z_]*.(cc|h):[0-9]*", "[asan_rtl]", file_name)
     57   file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
     58   return file_name
     59 
     60 # TODO(glider): need some refactoring here
     61 def symbolize_addr2line(line, binary_prefix, paths_to_cut):
     62   global next_inline_frameno
     63   # Strip the log prefix ("I/asanwrapper( 1196): ").
     64   line = re.sub(r'^.*?: ', '', line)
     65   #0 0x7f6e35cf2e45  (/blah/foo.so+0x11fe45)
     66   match = re.match(r'^(\s*#)([0-9]+) *(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line, re.UNICODE)
     67   if match:
     68     frameno = match.group(2)
     69     binary = match.group(4)
     70     addr = match.group(5)
     71     addr = patch_address(frameno, addr)
     72 
     73     if binary.startswith('/'):
     74       binary = binary[1:]
     75     binary = os.path.join(binary_prefix, binary)
     76 
     77     if not os.path.exists(binary):
     78       print line.rstrip().encode('utf-8')
     79       return
     80 
     81     load_addr = android_get_load_address(binary)
     82     addr = hex(int(addr, 16) + load_addr)
     83 
     84     if not pipes.has_key(binary):
     85       pipes[binary] = subprocess.Popen(["addr2line", "-i", "-f", "-e", binary],
     86                          stdin=subprocess.PIPE, stdout=subprocess.PIPE)
     87     p = pipes[binary]
     88     frames = []
     89     try:
     90       print >>p.stdin, addr
     91       # This will trigger a "??" response from addr2line so we know when to stop
     92       print >>p.stdin
     93       while True:
     94         function_name = p.stdout.readline().rstrip()
     95         file_name     = p.stdout.readline().rstrip()
     96         if function_name in ['??', '']:
     97           break
     98         file_name = postprocess_file_name(file_name, paths_to_cut)
     99         frames.append((function_name, file_name))
    100     except:
    101       pass
    102     if not frames:
    103       frames.append(('', ''))
    104       # Consume another pair of "??" lines
    105       try:
    106         p.stdout.readline()
    107         p.stdout.readline()
    108       except:
    109         pass
    110     for frame in frames:
    111       inline_frameno = next_inline_frameno
    112       next_inline_frameno += 1
    113       print "%s%d" % (match.group(1).encode('utf-8'), inline_frameno), \
    114           match.group(3).encode('utf-8'), "in", frame[0], frame[1]
    115   else:
    116     print line.rstrip().encode('utf-8')
    117 
    118 
    119 binary_prefix = os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'symbols')
    120 paths_to_cut = [os.getcwd() + '/', os.environ['ANDROID_BUILD_TOP'] + '/'] + sys.argv[1:]
    121 
    122 for line in sys.stdin:
    123   line = line.decode('utf-8')
    124   symbolize_addr2line(line, binary_prefix, paths_to_cut)
    125