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#===------------------------------------------------------------------------===# 10import glob 11import os 12import re 13import sys 14import string 15import subprocess 16 17pipes = {} 18next_inline_frameno = 0 19 20def 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 35def 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 43def 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 100binary_prefix = os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'symbols') 101paths_to_cut = [os.getcwd() + '/', os.environ['ANDROID_BUILD_TOP'] + '/'] + sys.argv[1:] 102 103for line in sys.stdin: 104 line = line.decode('utf-8', 'replace') 105 symbolize_addr2line(line, binary_prefix, paths_to_cut) 106