[TTC-2] Fix asan_symbolize.py for C++ function prototypes detection.
[platform/upstream/gcc.git] / packaging / asan_symbolize.py
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 https://github.com/llvm-mirror/compiler-rt/blob/master/LICENSE.TXT
8 # for details.
9 #
10 #===------------------------------------------------------------------------===#
11 import argparse
12 import array
13 import binascii
14 import bisect
15 import os
16 import re
17 import subprocess
18 import sys
19
20 symbolizers = {}
21 DEBUG = False
22 demangle = False
23 binutils_prefix = None
24 sysroot_path = None
25 binary_name_filter = None
26 fix_filename_patterns = None
27 logfile = sys.stdin
28 separate_debug_dir_list = []
29 prelink_offset = None
30
31 # FIXME: merge the code that calls fix_filename().
32 def fix_filename(file_name):
33   if fix_filename_patterns:
34     for path_to_cut in fix_filename_patterns:
35       file_name = re.sub('.*' + path_to_cut, '', file_name)
36   file_name = re.sub('.*asan_[a-z_]*.cc:[0-9]*', '_asan_rtl_', file_name)
37   file_name = re.sub('.*crtstuff.c:0', '???:0', file_name)
38   return file_name
39
40 def sysroot_path_filter(binary_name):
41   return sysroot_path + binary_name
42
43 def use_binutils_prefix(tool_name):
44   if binutils_prefix:
45     tool_name = binutils_prefix + tool_name
46   return tool_name
47
48 def print_error_message(message):
49   print >> sys.stderr, 'Error occured during symbolizisation: ' + message
50
51 class DebugInfoHandler(object):
52   def __init__(self, binary_name_filter=None):
53     self.binary_name_filter = binary_name_filter
54     self.global_debug_dir_list = [ self.use_name_filter("/usr/lib/debug") ]
55     if separate_debug_dir_list:
56       self.global_debug_dir_list = separate_debug_dir_list
57
58   def use_name_filter(self, binary_name):
59     if self.binary_name_filter:
60       binary_name = self.binary_name_filter(binary_name)
61     return binary_name
62
63   def calc_crc32(self, filename):
64     buf = open(filename,'rb').read()
65     buf = (binascii.crc32(buf, 0) & 0xFFFFFFFF)
66     return "%08X" % buf
67
68   def readelf_binary(self, options, binary):
69     cmd = [use_binutils_prefix('readelf'), options,'-W', binary]
70     try:
71       process = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=open(os.devnull, 'w'))
72     except:
73       print_error_message('the following command failed:\n''"' + ' '.join(cmd) + '"')
74       return
75     (readelf_out, _) = process.communicate()
76     if process.returncode == 0:
77       return readelf_out
78
79   def has_debuginfo(self, binary):
80     readelf_out = self.readelf_binary('-S', binary)
81     return  readelf_out and (".debug_" in readelf_out)
82
83   def get_buildid(self, binary):
84     readelf_out = self.readelf_binary('-nw', binary)
85     '''
86     Build ID is 40-length hex value following after "Build ID:".
87     e.g.:
88     Notes at offset 0x00000274 with length 0x00000024:
89       Owner                 Data size       Description
90       GNU                  0x00000014       NT_GNU_BUILD_ID (unique build ID bitstring)
91             Build ID: 977b1d1375ba6791d5f6dd38d8d55b95f9fca33a
92     '''
93     if readelf_out:
94       readelf_lines = readelf_out.split("\n");
95       buildid_lines = filter(re.compile('Build ID:').search, readelf_lines)
96       for line in buildid_lines:
97         match = re.search('[a-f0-9]{40}', line)
98         if match:
99           return match.group(0)
100       else:
101           print_error_message('failed to read Build ID value from ' + binary)
102
103   def get_debuglink_name(self, binary):
104     readelf_out = self.readelf_binary('--string-dump=.gnu_debuglink', binary)
105     '''
106     "debug link" is the last word in the first line of dump.
107     e.g.:
108     String dump of section '.gnu_debuglink':
109       [     0]  HeapOutOfBounds.out.debug
110     '''
111     if readelf_out:
112       readelf_lines = filter(None, readelf_out.split("\n"));
113       headline ="String dump of section '.gnu_debuglink':"
114       try:
115         debuglink_line_idx = readelf_lines.index(headline) + 1
116       except ValueError:
117         # There is no gnu_debuglink section in this binary
118         return
119       if len(readelf_lines) > debuglink_line_idx:
120         return readelf_lines[debuglink_line_idx].split()[-1]
121       else:
122         print_error_message('failed to read debuglink value from ' + binary)
123
124   def get_debuglink_crc(self, binary):
125     readelf_out = self.readelf_binary('--hex-dump=.gnu_debuglink', binary)
126     '''
127     crc is the last hex group (before string representation) in the last line of dump.
128     e.g. (crc is f89f21c2) :
129     Hex dump of section '.gnu_debuglink':
130       0x00000000 48656170 4f75744f 66426f75 6e64732e HeapOutOfBounds.
131       0x00000010 6f75742e 64656275 67000000 f89f21c2 out.debug.....!.
132     '''
133     if readelf_out:
134       # get last non-empty line
135       crc_line = filter(None, readelf_out.split("\n"))[-1]
136       # remove last 16 characters (string dump) from line
137       crc_line = crc_line[:-16]
138       # crc is last word in string
139       crc = crc_line.split()[-1]
140       match = re.match('[a-f0-9]{8}', crc)
141       if match:
142         if sys.byteorder == 'little':
143           crc = array.array('i', binascii.unhexlify(crc) )
144           crc.byteswap()
145           crc = binascii.hexlify(crc)
146         return crc
147       else:
148         print_error_message('failed to get crc checksum from debuglink in ' + binary)
149
150
151   def is_prelinked(self, binary):
152     readelf_out = self.readelf_binary('-S', binary)
153     return readelf_out and ".gnu.prelink_undo" in readelf_out
154
155   def get_load_address(self, binary):
156     readelf_out = self.readelf_binary('-l', binary)
157     '''
158     Readelf program headers output example:
159     Elf file type is DYN (Shared object file)
160     Entry point 0xb1160668
161     There are 10 program headers, starting at offset 52
162     Program Headers:
163       Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align
164      EXIDX          0x124754 0xb126c754 0xb126c754 0x04498 0x04498 R   0x4
165      [...]
166      LOAD           0x000000 0xb1148000 0xb1148000 0x12be9c 0x12be9c R E 0x8000
167
168     '''
169     readelf_lines = readelf_out.split("\n");
170     load_lines = filter(re.compile(
171       '[\s]*LOAD[\s]+0x[0]+[\s]+0x[a-fA-f\d]+').match, readelf_lines)
172     if load_lines:
173       return load_lines[0].split()[2]
174     else:
175       print_error_message('failed to get load address in ' + binary)
176
177   def get_prelink_offset(self, orig, prelinked):
178     if not self.is_prelinked(prelinked):
179       return
180     orig_load_addr = self.get_load_address(orig)
181     prelinked_load_addr = self.get_load_address(prelinked)
182     return int(prelinked_load_addr, 16) - int(orig_load_addr, 16)
183
184   def locate_in_orig_dir(self, debuglink_name, orig_binary_name):
185     debuginfo = os.path.join(os.path.dirname(orig_binary_name), debuglink_name)
186     debuginfo = self.use_name_filter(debuginfo)
187     return [debuginfo]
188
189   def locate_in_debug_subdir(self, debuglink_name, orig_binary_name):
190     debuginfo = os.path.join(os.path.dirname(orig_binary_name), '.debug', debuglink_name)
191     debuginfo = self.use_name_filter(debuginfo)
192     return [debuginfo]
193
194   def locate_in_global_debug_dir(self, debuglink_name, orig_binary_name):
195     debuginfo_list = []
196     for global_debug_dir in self.global_debug_dir_list:
197       debuginfo = global_debug_dir + os.path.join(os.path.dirname(orig_binary_name), debuglink_name)
198       debuginfo_list.append(debuginfo)
199     return debuginfo_list
200
201   def locate_in_buildid_dir(self, debuglink_name, orig_binary_name):
202     dir_name = debuglink_name[0:2]
203     file_name = debuglink_name[2:] + ".debug"
204     debuginfo_list = []
205     for global_debug_dir in self.global_debug_dir_list:
206       debuginfo = os.path.join(global_debug_dir, ".build-id", dir_name, file_name)
207       debuginfo_list.append(debuginfo)
208     return debuginfo_list
209
210   def get_debuginfo(self,binary_name):
211     global prelink_offset
212     prelink_offset = None
213     orig_binary_name = binary_name
214     # First apply sysroot path if defined to get real binary
215     real_binary = self.use_name_filter(binary_name)
216     if self.has_debuginfo(real_binary):
217       return real_binary
218     # Look for debuginfo file according to GDB rules
219     # 1) "build-id" method
220     buildid_name = self.get_buildid(real_binary)
221     debugfile_list = []
222     if buildid_name:
223       debugfile_list = self.locate_in_buildid_dir(buildid_name, orig_binary_name)
224       for debugfile in debugfile_list:
225         if os.path.isfile(debugfile):
226           prelink_offset = self.get_prelink_offset(debugfile, real_binary)
227           return debugfile
228     # 2) "debug link" method
229     debuglink_name = self.get_debuglink_name(real_binary)
230     debuglink_crc = self.get_debuglink_crc(real_binary)
231     debugfile_list = []
232     if debuglink_name and debuglink_crc:
233       debuglink_locate_list = [
234         self.locate_in_orig_dir,
235         self.locate_in_debug_subdir,
236         self.locate_in_global_debug_dir ]
237       for debuglink_locate_function in debuglink_locate_list:
238         debugfile_list = debuglink_locate_function(debuglink_name, orig_binary_name)
239         for debugfile in debugfile_list:
240           if os.path.isfile(debugfile):
241             debugfile_crc = self.calc_crc32(debugfile)
242             if int(debuglink_crc,16) == int(debugfile_crc, 16):
243               prelink_offset = self.get_prelink_offset(debugfile, real_binary)
244               return debugfile
245     return real_binary
246
247 def guess_arch(addr):
248   # Guess which arch we're running. 10 = len('0x') + 8 hex digits.
249   if len(addr) > 10:
250     return 'x86_64'
251   else:
252     return 'i386'
253
254 class Symbolizer(object):
255   def __init__(self):
256     pass
257
258   def symbolize(self, addr, binary, offset):
259     """Symbolize the given address (pair of binary and offset).
260
261     Overriden in subclasses.
262     Args:
263         addr: virtual address of an instruction.
264         binary: path to executable/shared object containing this instruction.
265         offset: instruction offset in the @binary.
266     Returns:
267         list of strings (one string for each inlined frame) describing
268         the code locations for this instruction (that is, function name, file
269         name, line and column numbers).
270     """
271     return None
272
273
274 class LLVMSymbolizer(Symbolizer):
275   def __init__(self, symbolizer_path, default_arch, system, dsym_hints=[]):
276     super(LLVMSymbolizer, self).__init__()
277     self.symbolizer_path = symbolizer_path
278     self.default_arch = default_arch
279     self.system = system
280     self.dsym_hints = dsym_hints
281     self.pipe = self.open_llvm_symbolizer()
282
283   def open_llvm_symbolizer(self):
284     cmd = [self.symbolizer_path,
285            '--use-symbol-table=true',
286            '--demangle=%s' % demangle,
287            '--functions=short',
288            '--inlining=true',
289            '--default-arch=%s' % self.default_arch]
290     if self.system == 'Darwin':
291       for hint in self.dsym_hints:
292         cmd.append('--dsym-hint=%s' % hint)
293     if DEBUG:
294       print ' '.join(cmd)
295     try:
296       result = subprocess.Popen(cmd, stdin=subprocess.PIPE,
297                                 stdout=subprocess.PIPE)
298     except OSError:
299       result = None
300     return result
301
302   def symbolize(self, addr, binary, offset):
303     """Overrides Symbolizer.symbolize."""
304     if not self.pipe:
305       return None
306     result = []
307     try:
308       symbolizer_input = '"%s" %s' % (binary, offset)
309       if DEBUG:
310         print symbolizer_input
311       print >> self.pipe.stdin, symbolizer_input
312       while True:
313         function_name = self.pipe.stdout.readline().rstrip()
314         if not function_name:
315           break
316         file_name = self.pipe.stdout.readline().rstrip()
317         file_name = fix_filename(file_name)
318         if (not function_name.startswith('??') or
319             not file_name.startswith('??')):
320           # Append only non-trivial frames.
321           result.append('%s in %s %s' % (addr, function_name,
322                                          file_name))
323     except Exception:
324       result = []
325     if not result:
326       result = None
327     return result
328
329
330 def LLVMSymbolizerFactory(system, default_arch, dsym_hints=[]):
331   symbolizer_path = os.getenv('LLVM_SYMBOLIZER_PATH')
332   if not symbolizer_path:
333     symbolizer_path = os.getenv('ASAN_SYMBOLIZER_PATH')
334     if not symbolizer_path:
335       # Assume llvm-symbolizer is in PATH.
336       symbolizer_path = 'llvm-symbolizer'
337   return LLVMSymbolizer(symbolizer_path, default_arch, system, dsym_hints)
338
339
340 class Addr2LineSymbolizer(Symbolizer):
341   def __init__(self, binary):
342     super(Addr2LineSymbolizer, self).__init__()
343     self.binary = binary
344
345   def symbolize(self, addr, binary, offset):
346     """Overrides Symbolizer.symbolize."""
347     cmd = [use_binutils_prefix('addr2line'), '-fi']
348     if demangle:
349       cmd += ['--demangle']
350     cmd += ['-e', self.binary, offset]
351     if DEBUG:
352       print ' '.join(cmd)
353     self.pipe = subprocess.Popen(cmd, stdout=subprocess.PIPE)
354     result = []
355     if self.binary != binary:
356       return None
357     try:
358       lines = self.pipe.stdout.readlines()
359     except Exception:
360       lines = []
361     if not lines:
362       lines.append('??')
363       lines.append('??:?')
364     for i in range(0, len(lines), 2):
365       function_name = lines[i].rstrip()
366       file_name = fix_filename(lines[i+1].rstrip())
367       result.append('%s in %s %s' % (addr, function_name, file_name))
368     return result
369
370
371 class UnbufferedLineConverter(object):
372   """
373   Wrap a child process that responds to each line of input with one line of
374   output.  Uses pty to trick the child into providing unbuffered output.
375   """
376   def __init__(self, args, close_stderr=False):
377     # Local imports so that the script can start on Windows.
378     import pty
379     import termios
380     pid, fd = pty.fork()
381     if pid == 0:
382       # We're the child. Transfer control to command.
383       if close_stderr:
384         dev_null = os.open('/dev/null', 0)
385         os.dup2(dev_null, 2)
386       os.execvp(args[0], args)
387     else:
388       # Disable echoing.
389       attr = termios.tcgetattr(fd)
390       attr[3] = attr[3] & ~termios.ECHO
391       termios.tcsetattr(fd, termios.TCSANOW, attr)
392       # Set up a file()-like interface to the child process
393       self.r = os.fdopen(fd, "r", 1)
394       self.w = os.fdopen(os.dup(fd), "w", 1)
395
396   def convert(self, line):
397     self.w.write(line + "\n")
398     return self.readline()
399
400   def readline(self):
401     return self.r.readline().rstrip()
402
403
404 class DarwinSymbolizer(Symbolizer):
405   def __init__(self, addr, binary):
406     super(DarwinSymbolizer, self).__init__()
407     self.binary = binary
408     self.arch = guess_arch(addr)
409     self.open_atos()
410
411   def open_atos(self):
412     if DEBUG:
413       print 'atos -o %s -arch %s' % (self.binary, self.arch)
414     cmdline = ['atos', '-o', self.binary, '-arch', self.arch]
415     self.atos = UnbufferedLineConverter(cmdline, close_stderr=True)
416
417   def symbolize(self, addr, binary, offset):
418     """Overrides Symbolizer.symbolize."""
419     if self.binary != binary:
420       return None
421     atos_line = self.atos.convert('0x%x' % int(offset, 16))
422     while "got symbolicator for" in atos_line:
423       atos_line = self.atos.readline()
424     # A well-formed atos response looks like this:
425     #   foo(type1, type2) (in object.name) (filename.cc:80)
426     match = re.match('^(.*) \(in (.*)\) \((.*:\d*)\)$', atos_line)
427     if DEBUG:
428       print 'atos_line: ', atos_line
429     if match:
430       function_name = match.group(1)
431       function_name = re.sub('\(.*?\)', '', function_name)
432       file_name = fix_filename(match.group(3))
433       return ['%s in %s %s' % (addr, function_name, file_name)]
434     else:
435       return ['%s in %s' % (addr, atos_line)]
436
437
438 # Chain several symbolizers so that if one symbolizer fails, we fall back
439 # to the next symbolizer in chain.
440 class ChainSymbolizer(Symbolizer):
441   def __init__(self, symbolizer_list):
442     super(ChainSymbolizer, self).__init__()
443     self.symbolizer_list = symbolizer_list
444
445   def symbolize(self, addr, binary, offset):
446     """Overrides Symbolizer.symbolize."""
447     for symbolizer in self.symbolizer_list:
448       if symbolizer:
449         result = symbolizer.symbolize(addr, binary, offset)
450         if result:
451           return result
452     return None
453
454   def append_symbolizer(self, symbolizer):
455     self.symbolizer_list.append(symbolizer)
456
457
458 def BreakpadSymbolizerFactory(binary):
459   suffix = os.getenv('BREAKPAD_SUFFIX')
460   if suffix:
461     filename = binary + suffix
462     if os.access(filename, os.F_OK):
463       return BreakpadSymbolizer(filename)
464   return None
465
466
467 def SystemSymbolizerFactory(system, addr, binary):
468   if system == 'Darwin':
469     return DarwinSymbolizer(addr, binary)
470   elif system == 'Linux':
471     return Addr2LineSymbolizer(binary)
472
473
474 class BreakpadSymbolizer(Symbolizer):
475   def __init__(self, filename):
476     super(BreakpadSymbolizer, self).__init__()
477     self.filename = filename
478     lines = file(filename).readlines()
479     self.files = []
480     self.symbols = {}
481     self.address_list = []
482     self.addresses = {}
483     # MODULE mac x86_64 A7001116478B33F18FF9BEDE9F615F190 t
484     fragments = lines[0].rstrip().split()
485     self.arch = fragments[2]
486     self.debug_id = fragments[3]
487     self.binary = ' '.join(fragments[4:])
488     self.parse_lines(lines[1:])
489
490   def parse_lines(self, lines):
491     cur_function_addr = ''
492     for line in lines:
493       fragments = line.split()
494       if fragments[0] == 'FILE':
495         assert int(fragments[1]) == len(self.files)
496         self.files.append(' '.join(fragments[2:]))
497       elif fragments[0] == 'PUBLIC':
498         self.symbols[int(fragments[1], 16)] = ' '.join(fragments[3:])
499       elif fragments[0] in ['CFI', 'STACK']:
500         pass
501       elif fragments[0] == 'FUNC':
502         cur_function_addr = int(fragments[1], 16)
503         if not cur_function_addr in self.symbols.keys():
504           self.symbols[cur_function_addr] = ' '.join(fragments[4:])
505       else:
506         # Line starting with an address.
507         addr = int(fragments[0], 16)
508         self.address_list.append(addr)
509         # Tuple of symbol address, size, line, file number.
510         self.addresses[addr] = (cur_function_addr,
511                                 int(fragments[1], 16),
512                                 int(fragments[2]),
513                                 int(fragments[3]))
514     self.address_list.sort()
515
516   def get_sym_file_line(self, addr):
517     key = None
518     if addr in self.addresses.keys():
519       key = addr
520     else:
521       index = bisect.bisect_left(self.address_list, addr)
522       if index == 0:
523         return None
524       else:
525         key = self.address_list[index - 1]
526     sym_id, size, line_no, file_no = self.addresses[key]
527     symbol = self.symbols[sym_id]
528     filename = self.files[file_no]
529     if addr < key + size:
530       return symbol, filename, line_no
531     else:
532       return None
533
534   def symbolize(self, addr, binary, offset):
535     if self.binary != binary:
536       return None
537     res = self.get_sym_file_line(int(offset, 16))
538     if res:
539       function_name, file_name, line_no = res
540       result = ['%s in %s %s:%d' % (
541           addr, function_name, file_name, line_no)]
542       print result
543       return result
544     else:
545       return None
546
547
548 class SymbolizationLoop(object):
549   def __init__(self, binary_name_filter=None, dsym_hint_producer=None):
550     if sys.platform == 'win32':
551       # ASan on Windows uses dbghelp.dll to symbolize in-process, which works
552       # even in sandboxed processes.  Nothing needs to be done here.
553       self.process_line = self.process_line_echo
554     else:
555       # Used by clients who may want to supply a different binary name.
556       # E.g. in Chrome several binaries may share a single .dSYM.
557       self.binary_name_filter = binary_name_filter
558       self.dsym_hint_producer = dsym_hint_producer
559       self.system = os.uname()[0]
560       if self.system not in ['Linux', 'Darwin', 'FreeBSD']:
561         raise Exception('Unknown system')
562       self.llvm_symbolizers = {}
563       self.last_llvm_symbolizer = None
564       self.dsym_hints = set([])
565       self.frame_no = 0
566       self.process_line = self.process_line_posix
567
568   def symbolize_address(self, addr, binary, offset):
569     # On non-Darwin (i.e. on platforms without .dSYM debug info) always use
570     # a single symbolizer binary.
571     # On Darwin, if the dsym hint producer is present:
572     #  1. check whether we've seen this binary already; if so,
573     #     use |llvm_symbolizers[binary]|, which has already loaded the debug
574     #     info for this binary (might not be the case for
575     #     |last_llvm_symbolizer|);
576     #  2. otherwise check if we've seen all the hints for this binary already;
577     #     if so, reuse |last_llvm_symbolizer| which has the full set of hints;
578     #  3. otherwise create a new symbolizer and pass all currently known
579     #     .dSYM hints to it.
580     if not binary in self.llvm_symbolizers:
581       use_new_symbolizer = True
582       if self.system == 'Darwin' and self.dsym_hint_producer:
583         dsym_hints_for_binary = set(self.dsym_hint_producer(binary))
584         use_new_symbolizer = bool(dsym_hints_for_binary - self.dsym_hints)
585         self.dsym_hints |= dsym_hints_for_binary
586       if self.last_llvm_symbolizer and not use_new_symbolizer:
587           self.llvm_symbolizers[binary] = self.last_llvm_symbolizer
588       else:
589         self.last_llvm_symbolizer = LLVMSymbolizerFactory(
590             self.system, guess_arch(addr), self.dsym_hints)
591         self.llvm_symbolizers[binary] = self.last_llvm_symbolizer
592     # Use the chain of symbolizers:
593     # Breakpad symbolizer -> LLVM symbolizer -> addr2line/atos
594     # (fall back to next symbolizer if the previous one fails).
595     if not binary in symbolizers:
596       symbolizers[binary] = ChainSymbolizer(
597           [BreakpadSymbolizerFactory(binary), self.llvm_symbolizers[binary]])
598     result = symbolizers[binary].symbolize(addr, binary, offset)
599     if result is None:
600       # Initialize system symbolizer only if other symbolizers failed.
601       symbolizers[binary].append_symbolizer(
602           SystemSymbolizerFactory(self.system, addr, binary))
603       result = symbolizers[binary].symbolize(addr, binary, offset)
604     # The system symbolizer must produce some result.
605     assert result
606     return result
607
608   def get_symbolized_lines(self, symbolized_lines):
609     if not symbolized_lines:
610       return [self.current_line]
611     else:
612       result = []
613       for symbolized_frame in symbolized_lines:
614         if '?' in symbolized_frame:
615           symbolized_frame += " " + re.search('\(.*?\)',self.current_line).group(0)
616         result.append('    #%s %s' % (str(self.frame_no), symbolized_frame.rstrip()))
617         self.frame_no += 1
618       return result
619
620   def process_logfile(self):
621     self.frame_no = 0
622     for line in logfile:
623       processed = self.process_line(line)
624       print '\n'.join(processed)
625
626   def process_line_echo(self, line):
627     return [line.rstrip()]
628
629   def process_line_posix(self, line):
630     self.current_line = line.rstrip()
631     #0 0x7f6e35cf2e45  (/blah/foo.so+0x11fe45)
632     stack_trace_line_format = (
633         '^( *#([0-9]+) *)(0x[0-9a-f]+)( *in [^/]+)? *\((.*)\+(0x[0-9a-f]+)\)')
634     match = re.match(stack_trace_line_format, line)
635     if not match:
636       return [self.current_line]
637     if DEBUG:
638       print line
639     _, frameno_str, addr, func, binary, offset = match.groups()
640     if frameno_str == '0':
641       # Assume that frame #0 is the first frame of new stack trace.
642       self.frame_no = 0
643     original_binary = binary
644     if self.binary_name_filter:
645       binary = self.binary_name_filter(binary)
646     # Correct offset from backtrace if the binary was prelinked
647     # and printed address considers the prelink offset:
648     if prelink_offset:
649       real_offset = int(offset,16)
650       if real_offset > prelink_offset:
651         #FIXME: Need to check that offset fits section size
652         offset = hex(real_offset - prelink_offset)
653         if DEBUG:
654           print 'real address: ' + offset
655     symbolized_line = self.symbolize_address(addr, binary, offset)
656     if not symbolized_line:
657       if original_binary != binary:
658         symbolized_line = self.symbolize_address(addr, binary, offset)
659     return self.get_symbolized_lines(symbolized_line)
660
661
662 if __name__ == '__main__':
663   parser = argparse.ArgumentParser(
664       formatter_class=argparse.RawDescriptionHelpFormatter,
665       description='ASan symbolization script',
666       epilog='Example of use:\n'
667              'asan_symbolize.py -c "$HOME/opt/cross/bin/armv7l-tizen-linux-gnueabi-" '
668              '-s "$HOME/SymbolFiles" < asan.log')
669   parser.add_argument('path_to_cut', nargs='*',
670                       help='pattern to be cut from the result file path ')
671   parser.add_argument('-d','--demangle', action='store_true',
672                       help='demangle function names')
673   parser.add_argument('-s', metavar='SYSROOT',
674                       help='set path to sysroot for sanitized binaries')
675   parser.add_argument('-c', metavar='CROSS_COMPILE',
676                       help='set prefix for binutils')
677   parser.add_argument('-l','--logfile', default=sys.stdin,
678                       type=argparse.FileType('r'),
679                       help='set log file name to parse, default is stdin')
680   parser.add_argument('-y', '--debug-file-directory', metavar='DEBUGDIR',
681                       help='The directories for separate debug information \
682                       files. Multiple path components can be set concatenating \
683                       them by a path separator.')
684   args = parser.parse_args()
685   if args.path_to_cut:
686     fix_filename_patterns = args.path_to_cut
687   if args.demangle:
688     demangle = True
689   if args.s:
690     binary_name_filter = sysroot_path_filter
691     sysroot_path = args.s
692   if args.c:
693     binutils_prefix = args.c
694   if args.logfile:
695     logfile = args.logfile
696   else:
697     logfile = sys.stdin
698   if args.debug_file_directory:
699     separate_debug_dir_list = args.debug_file_directory.split(":")
700   if os.uname()[0] == 'Linux':
701     debug_info_handler = DebugInfoHandler(binary_name_filter)
702     binary_name_filter = debug_info_handler.get_debuginfo
703   loop = SymbolizationLoop(binary_name_filter)
704   loop.process_logfile()