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 os
     11 import re
     12 import sys
     13 import string
     14 import subprocess
     15 
     16 pipes = {}
     17 load_addresses = {}
     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 android_get_load_address(path):
     36   if load_addresses.has_key(path):
     37     return load_addresses[path]
     38   readelf = os.path.join(os.environ['ANDROID_EABI_TOOLCHAIN'], 'arm-linux-androideabi-readelf')
     39   readelf_pipe = subprocess.Popen([readelf, "-l", path], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
     40   for line in readelf_pipe.stdout:
     41       if ('LOAD' in line) and (' E ' in line):
     42           match = re.match(r'\s*LOAD\s+0x[01-9a-zA-Z]+\s+(0x[01-9a-zA-Z]+)', line, re.UNICODE)
     43           if match:
     44               load_addr = int(match.group(1), 16)
     45               load_addresses[path] = load_addr
     46               return load_addr
     47           else: break
     48   print 'Could not make sense of readelf output!'
     49   sys.exit(1)
     50 
     51 def postprocess_file_name(file_name, paths_to_cut):
     52   for path_to_cut in paths_to_cut:
     53     file_name = re.sub(".*" + path_to_cut, "", file_name)
     54   file_name = re.sub(".*asan_[a-z_]*.(cc|h):[0-9]*", "[asan_rtl]", file_name)
     55   file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
     56   return file_name
     57 
     58 # TODO(glider): need some refactoring here
     59 def symbolize_addr2line(line, binary_prefix, paths_to_cut):
     60   global next_inline_frameno
     61   # Strip the log prefix ("I/asanwrapper( 1196): ").
     62   line = re.sub(r'^[A-Z]/[^\s]*\(\s*\d+\): ', '', line)
     63   #0 0x7f6e35cf2e45  (/blah/foo.so+0x11fe45)
     64   match = re.match(r'^(\s*#)([0-9]+) *(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line, re.UNICODE)
     65   if match:
     66     frameno = match.group(2)
     67     binary = match.group(4)
     68     addr = match.group(5)
     69     addr = patch_address(frameno, addr)
     70 
     71     if binary.startswith('/'):
     72       binary = binary[1:]
     73     binary = os.path.join(binary_prefix, binary)
     74 
     75     load_addr = android_get_load_address(binary)
     76     addr = hex(int(addr, 16) + load_addr)
     77 
     78     if not pipes.has_key(binary):
     79       pipes[binary] = subprocess.Popen(["addr2line", "-i", "-f", "-e", binary],
     80                          stdin=subprocess.PIPE, stdout=subprocess.PIPE)
     81     p = pipes[binary]
     82     frames = []
     83     try:
     84       print >>p.stdin, addr
     85       # This will trigger a "??" response from addr2line so we know when to stop
     86       print >>p.stdin
     87       while True:
     88         function_name = p.stdout.readline().rstrip()
     89         file_name     = p.stdout.readline().rstrip()
     90         if function_name in ['??', '']:
     91           break
     92         file_name = postprocess_file_name(file_name, paths_to_cut)
     93         frames.append((function_name, file_name))
     94     except:
     95       pass
     96     if not frames:
     97       frames.append(('', ''))
     98       # Consume another pair of "??" lines
     99       try:
    100         p.stdout.readline()
    101         p.stdout.readline()
    102       except:
    103         pass
    104     for frame in frames:
    105       inline_frameno = next_inline_frameno
    106       next_inline_frameno += 1
    107       print "%s%d" % (match.group(1).encode('utf-8'), inline_frameno), \
    108           match.group(3).encode('utf-8'), "in", frame[0], frame[1]
    109   else:
    110     print line.rstrip().encode('utf-8')
    111 
    112 
    113 binary_prefix = os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'symbols')
    114 paths_to_cut = [os.getcwd() + '/', os.environ['ANDROID_BUILD_TOP'] + '/'] + sys.argv[1:]
    115 
    116 for line in sys.stdin:
    117   line = line.decode('utf-8')
    118   symbolize_addr2line(line, binary_prefix, paths_to_cut)
    119