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