1dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov#!/usr/bin/env python
2dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov#===- lib/asan/scripts/asan_symbolize.py -----------------------------------===#
3dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov#
4dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov#                     The LLVM Compiler Infrastructure
5dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov#
6dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov# This file is distributed under the University of Illinois Open Source
7dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov# License. See LICENSE.TXT for details.
8dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov#
9dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov#===------------------------------------------------------------------------===#
10dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovimport os
11dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovimport re
12dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovimport sys
13dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovimport string
14dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovimport subprocess
15dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
16dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovpipes = {}
17dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovload_addresses = {}
1846d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanovnext_inline_frameno = 0
19dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
20dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovdef patch_address(frameno, addr_s):
21dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  ''' Subtracts 1 or 2 from the top frame's address.
22dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  Top frame is normally the return address from asan_report*
23dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  call, which is not expected to return at all. Because of that, this
24dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  address often belongs to the next source code line, or even to a different
25dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  function. '''
26dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  if frameno == '0':
27dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    addr = int(addr_s, 16)
28dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    if os.uname()[4].startswith('arm'):
29dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov      # Cancel the Thumb bit
30dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov      addr = addr & (~1)
31dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    addr -= 1
32dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    return hex(addr)
33dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  return addr_s
34dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
35dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovdef android_get_load_address(path):
36dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  if load_addresses.has_key(path):
37dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    return load_addresses[path]
38dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  readelf = os.path.join(os.environ['ANDROID_EABI_TOOLCHAIN'], 'arm-linux-androideabi-readelf')
39dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  readelf_pipe = subprocess.Popen([readelf, "-l", path], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
40dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  for line in readelf_pipe.stdout:
41dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov      if ('LOAD' in line) and (' E ' in line):
42dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov          match = re.match(r'\s*LOAD\s+0x[01-9a-zA-Z]+\s+(0x[01-9a-zA-Z]+)', line, re.UNICODE)
43dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov          if match:
44dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov              load_addr = int(match.group(1), 16)
45dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov              load_addresses[path] = load_addr
46dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov              return load_addr
47dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov          else: break
48dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  print 'Could not make sense of readelf output!'
49dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  sys.exit(1)
50dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
5146d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanovdef postprocess_file_name(file_name, paths_to_cut):
5246d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov  for path_to_cut in paths_to_cut:
5346d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov    file_name = re.sub(".*" + path_to_cut, "", file_name)
5446d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov  file_name = re.sub(".*asan_[a-z_]*.(cc|h):[0-9]*", "[asan_rtl]", file_name)
5546d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov  file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
5646d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov  return file_name
5746d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov
58dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov# TODO(glider): need some refactoring here
59dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovdef symbolize_addr2line(line, binary_prefix, paths_to_cut):
6046d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov  global next_inline_frameno
61dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  # Strip the log prefix ("I/asanwrapper( 1196): ").
62dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  line = re.sub(r'^[A-Z]/[^\s]*\(\s*\d+\): ', '', line)
63dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  #0 0x7f6e35cf2e45  (/blah/foo.so+0x11fe45)
6446d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov  match = re.match(r'^(\s*#)([0-9]+) *(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line, re.UNICODE)
65dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  if match:
66dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    frameno = match.group(2)
6746d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov    binary = match.group(4)
6846d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov    addr = match.group(5)
69dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    addr = patch_address(frameno, addr)
70dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
71dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    if binary.startswith('/'):
72dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov      binary = binary[1:]
73dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    binary = os.path.join(binary_prefix, binary)
74dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
75dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    load_addr = android_get_load_address(binary)
76dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    addr = hex(int(addr, 16) + load_addr)
77dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
78dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    if not pipes.has_key(binary):
7946d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      pipes[binary] = subprocess.Popen(["addr2line", "-i", "-f", "-e", binary],
80dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov                         stdin=subprocess.PIPE, stdout=subprocess.PIPE)
81dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    p = pipes[binary]
8246d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov    frames = []
83dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    try:
84dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov      print >>p.stdin, addr
8546d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      # This will trigger a "??" response from addr2line so we know when to stop
8646d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      print >>p.stdin
8746d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      while True:
8846d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov        function_name = p.stdout.readline().rstrip()
8946d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov        file_name     = p.stdout.readline().rstrip()
9046d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov        if function_name in ['??', '']:
9146d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov          break
9246d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov        file_name = postprocess_file_name(file_name, paths_to_cut)
9346d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov        frames.append((function_name, file_name))
94dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    except:
9546d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      pass
9646d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov    if not frames:
9746d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      frames.append(('', ''))
9846d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      # Consume another pair of "??" lines
9946d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      try:
10046d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov        p.stdout.readline()
10146d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov        p.stdout.readline()
10246d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      except:
10346d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov        pass
10446d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov    for frame in frames:
10546d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      inline_frameno = next_inline_frameno
10646d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      next_inline_frameno += 1
10746d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov      print "%s%d" % (match.group(1).encode('utf-8'), inline_frameno), \
10846d7b0bd647fa4844f9fe4ca9be31b8341ad12f5Evgeniy Stepanov          match.group(3).encode('utf-8'), "in", frame[0], frame[1]
109dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  else:
110dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov    print line.rstrip().encode('utf-8')
111dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
112dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
113dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovbinary_prefix = os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'symbols')
114dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovpaths_to_cut = [os.getcwd() + '/', os.environ['ANDROID_BUILD_TOP'] + '/'] + sys.argv[1:]
115dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov
116dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanovfor line in sys.stdin:
117dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  line = line.decode('utf-8')
118dc3865b01cd56b703b77f58d5acff6529491eaf3Evgeniy Stepanov  symbolize_addr2line(line, binary_prefix, paths_to_cut)
119