aboutsummaryrefslogtreecommitdiff
path: root/lib/asan/scripts/symbolize.py
blob: ebd97a5678e940687964b594b3f397ecfc6d730a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#!/usr/bin/env python
#===- lib/asan/scripts/asan_symbolize.py -----------------------------------===#
#
#                     The LLVM Compiler Infrastructure
#
# This file is distributed under the University of Illinois Open Source
# License. See LICENSE.TXT for details.
#
#===------------------------------------------------------------------------===#
import glob
import os
import re
import sys
import string
import subprocess

pipes = {}
next_inline_frameno = 0

def patch_address(frameno, addr_s):
  ''' Subtracts 1 or 2 from the top frame's address.
  Top frame is normally the return address from asan_report*
  call, which is not expected to return at all. Because of that, this
  address often belongs to the next source code line, or even to a different
  function. '''
  if frameno == '0':
    addr = int(addr_s, 16)
    if os.uname()[4].startswith('arm'):
      # Cancel the Thumb bit
      addr = addr & (~1)
    addr -= 1
    return hex(addr)
  return addr_s

def postprocess_file_name(file_name, paths_to_cut):
  for path_to_cut in paths_to_cut:
    file_name = re.sub(".*" + path_to_cut, "", file_name)
  file_name = re.sub(".*asan_[a-z_]*.(cc|h):[0-9]*", "[asan_rtl]", file_name)
  file_name = re.sub(".*crtstuff.c:0", "???:0", file_name)
  return file_name

# TODO(glider): need some refactoring here
def symbolize_addr2line(line, binary_prefix, paths_to_cut):
  global next_inline_frameno
  # Strip the log prefix ("I/asanwrapper( 1196): ").
  line = re.sub(r'^.*?: ', '', line)
  #0 0x7f6e35cf2e45  (/blah/foo.so+0x11fe45)
  match = re.match(r'^(\s*#)([0-9]+) *(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)', line, re.UNICODE)
  if match:
    frameno = match.group(2)
    binary = match.group(4)
    addr = match.group(5)
    addr = patch_address(frameno, addr)

    if binary.startswith('/'):
      binary = binary[1:]
    binary = os.path.join(binary_prefix, binary)

    if not os.path.exists(binary):
      print line.rstrip().encode('utf-8')
      return

    addr = hex(int(addr, 16))

    if not pipes.has_key(binary):
      pipes[binary] = subprocess.Popen(["addr2line", "-i", "-f", "-e", binary],
                         stdin=subprocess.PIPE, stdout=subprocess.PIPE)
    p = pipes[binary]
    frames = []
    try:
      print >>p.stdin, addr
      # This will trigger a "??" response from addr2line so we know when to stop
      print >>p.stdin
      while True:
        function_name = p.stdout.readline().rstrip()
        file_name     = p.stdout.readline().rstrip()
        if function_name in ['??', '']:
          break
        file_name = postprocess_file_name(file_name, paths_to_cut)
        frames.append((function_name, file_name))
    except:
      pass
    if not frames:
      frames.append(('', ''))
      # Consume another pair of "??" lines
      try:
        p.stdout.readline()
        p.stdout.readline()
      except:
        pass
    for frame in frames:
      inline_frameno = next_inline_frameno
      next_inline_frameno += 1
      print "%s%d" % (match.group(1).encode('utf-8'), inline_frameno), \
          match.group(3).encode('utf-8'), "in", frame[0], frame[1]
  else:
    print line.rstrip().encode('utf-8')


binary_prefix = os.path.join(os.environ['ANDROID_PRODUCT_OUT'], 'symbols')
paths_to_cut = [os.getcwd() + '/', os.environ['ANDROID_BUILD_TOP'] + '/'] + sys.argv[1:]

for line in sys.stdin:
  line = line.decode('utf-8', 'replace')
  symbolize_addr2line(line, binary_prefix, paths_to_cut)