OLD | NEW |
1 #!/usr/bin/env python | 1 #!/usr/bin/env python |
2 #===- lib/asan/scripts/asan_symbolize.py -----------------------------------===# | 2 #===- lib/asan/scripts/asan_symbolize.py -----------------------------------===# |
3 # | 3 # |
4 # The LLVM Compiler Infrastructure | 4 # The LLVM Compiler Infrastructure |
5 # | 5 # |
6 # This file is distributed under the University of Illinois Open Source | 6 # This file is distributed under the University of Illinois Open Source |
7 # License. See LICENSE.TXT for details. | 7 # License. See LICENSE.TXT for details. |
8 # | 8 # |
9 #===------------------------------------------------------------------------===# | 9 #===------------------------------------------------------------------------===# |
10 import argparse | 10 import argparse |
11 import bisect | 11 import bisect |
12 import getopt | 12 import getopt |
13 import os | 13 import os |
14 import pty | |
15 import re | 14 import re |
16 import subprocess | 15 import subprocess |
17 import sys | 16 import sys |
18 import termios | |
19 | 17 |
20 symbolizers = {} | 18 symbolizers = {} |
21 DEBUG = False | 19 DEBUG = False |
22 demangle = False | 20 demangle = False |
23 binutils_prefix = None | 21 binutils_prefix = None |
24 sysroot_path = None | 22 sysroot_path = None |
25 binary_name_filter = None | 23 binary_name_filter = None |
26 fix_filename_patterns = None | 24 fix_filename_patterns = None |
27 logfile = sys.stdin | 25 logfile = sys.stdin |
28 | 26 |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
164 file_name = fix_filename(file_name) | 162 file_name = fix_filename(file_name) |
165 return ['%s in %s %s' % (addr, function_name, file_name)] | 163 return ['%s in %s %s' % (addr, function_name, file_name)] |
166 | 164 |
167 | 165 |
168 class UnbufferedLineConverter(object): | 166 class UnbufferedLineConverter(object): |
169 """ | 167 """ |
170 Wrap a child process that responds to each line of input with one line of | 168 Wrap a child process that responds to each line of input with one line of |
171 output. Uses pty to trick the child into providing unbuffered output. | 169 output. Uses pty to trick the child into providing unbuffered output. |
172 """ | 170 """ |
173 def __init__(self, args, close_stderr=False): | 171 def __init__(self, args, close_stderr=False): |
| 172 # Local imports so that the script can start on Windows. |
| 173 import pty |
| 174 import termios |
174 pid, fd = pty.fork() | 175 pid, fd = pty.fork() |
175 if pid == 0: | 176 if pid == 0: |
176 # We're the child. Transfer control to command. | 177 # We're the child. Transfer control to command. |
177 if close_stderr: | 178 if close_stderr: |
178 dev_null = os.open('/dev/null', 0) | 179 dev_null = os.open('/dev/null', 0) |
179 os.dup2(dev_null, 2) | 180 os.dup2(dev_null, 2) |
180 os.execvp(args[0], args) | 181 os.execvp(args[0], args) |
181 else: | 182 else: |
182 # Disable echoing. | 183 # Disable echoing. |
183 attr = termios.tcgetattr(fd) | 184 attr = termios.tcgetattr(fd) |
(...skipping 150 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
334 result = ['%s in %s %s:%d' % ( | 335 result = ['%s in %s %s:%d' % ( |
335 addr, function_name, file_name, line_no)] | 336 addr, function_name, file_name, line_no)] |
336 print result | 337 print result |
337 return result | 338 return result |
338 else: | 339 else: |
339 return None | 340 return None |
340 | 341 |
341 | 342 |
342 class SymbolizationLoop(object): | 343 class SymbolizationLoop(object): |
343 def __init__(self, binary_name_filter=None, dsym_hint_producer=None): | 344 def __init__(self, binary_name_filter=None, dsym_hint_producer=None): |
344 # Used by clients who may want to supply a different binary name. | 345 if sys.platform == 'win32': |
345 # E.g. in Chrome several binaries may share a single .dSYM. | 346 # ASan on Windows uses dbghelp.dll to symbolize in-process, which works |
346 self.binary_name_filter = binary_name_filter | 347 # even in sandboxed processes. Nothing needs to be done here. |
347 self.dsym_hint_producer = dsym_hint_producer | 348 self.process_line = self.process_line_echo |
348 self.system = os.uname()[0] | 349 else: |
349 if self.system not in ['Linux', 'Darwin', 'FreeBSD']: | 350 # Used by clients who may want to supply a different binary name. |
350 raise Exception('Unknown system') | 351 # E.g. in Chrome several binaries may share a single .dSYM. |
351 self.llvm_symbolizers = {} | 352 self.binary_name_filter = binary_name_filter |
352 self.last_llvm_symbolizer = None | 353 self.dsym_hint_producer = dsym_hint_producer |
353 self.dsym_hints = set([]) | 354 self.system = os.uname()[0] |
354 self.frame_no = 0 | 355 if self.system not in ['Linux', 'Darwin', 'FreeBSD']: |
| 356 raise Exception('Unknown system') |
| 357 self.llvm_symbolizers = {} |
| 358 self.last_llvm_symbolizer = None |
| 359 self.dsym_hints = set([]) |
| 360 self.frame_no = 0 |
| 361 self.process_line = self.process_line_posix |
355 | 362 |
356 def symbolize_address(self, addr, binary, offset): | 363 def symbolize_address(self, addr, binary, offset): |
357 # On non-Darwin (i.e. on platforms without .dSYM debug info) always use | 364 # On non-Darwin (i.e. on platforms without .dSYM debug info) always use |
358 # a single symbolizer binary. | 365 # a single symbolizer binary. |
359 # On Darwin, if the dsym hint producer is present: | 366 # On Darwin, if the dsym hint producer is present: |
360 # 1. check whether we've seen this binary already; if so, | 367 # 1. check whether we've seen this binary already; if so, |
361 # use |llvm_symbolizers[binary]|, which has already loaded the debug | 368 # use |llvm_symbolizers[binary]|, which has already loaded the debug |
362 # info for this binary (might not be the case for | 369 # info for this binary (might not be the case for |
363 # |last_llvm_symbolizer|); | 370 # |last_llvm_symbolizer|); |
364 # 2. otherwise check if we've seen all the hints for this binary already; | 371 # 2. otherwise check if we've seen all the hints for this binary already; |
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 return [self.current_line] | 405 return [self.current_line] |
399 else: | 406 else: |
400 result = [] | 407 result = [] |
401 for symbolized_frame in symbolized_lines: | 408 for symbolized_frame in symbolized_lines: |
402 result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstri
p())) | 409 result.append(' #%s %s' % (str(self.frame_no), symbolized_frame.rstri
p())) |
403 self.frame_no += 1 | 410 self.frame_no += 1 |
404 return result | 411 return result |
405 | 412 |
406 def process_logfile(self): | 413 def process_logfile(self): |
407 self.frame_no = 0 | 414 self.frame_no = 0 |
408 while True: | 415 for line in logfile: |
409 line = logfile.readline() | |
410 if not line: | |
411 break | |
412 processed = self.process_line(line) | 416 processed = self.process_line(line) |
413 print '\n'.join(processed) | 417 print '\n'.join(processed) |
414 | 418 |
415 def process_line(self, line): | 419 def process_line_echo(self, line): |
| 420 return [line.rstrip()] |
| 421 |
| 422 def process_line_posix(self, line): |
416 self.current_line = line.rstrip() | 423 self.current_line = line.rstrip() |
417 #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) | 424 #0 0x7f6e35cf2e45 (/blah/foo.so+0x11fe45) |
418 stack_trace_line_format = ( | 425 stack_trace_line_format = ( |
419 '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)') | 426 '^( *#([0-9]+) *)(0x[0-9a-f]+) *\((.*)\+(0x[0-9a-f]+)\)') |
420 match = re.match(stack_trace_line_format, line) | 427 match = re.match(stack_trace_line_format, line) |
421 if not match: | 428 if not match: |
422 return [self.current_line] | 429 return [self.current_line] |
423 if DEBUG: | 430 if DEBUG: |
424 print line | 431 print line |
425 _, frameno_str, addr, binary, offset = match.groups() | 432 _, frameno_str, addr, binary, offset = match.groups() |
426 if frameno_str == '0': | 433 if frameno_str == '0': |
427 # Assume that frame #0 is the first frame of new stack trace. | 434 # Assume that frame #0 is the first frame of new stack trace. |
428 self.frame_no = 0 | 435 self.frame_no = 0 |
429 original_binary = binary | 436 original_binary = binary |
430 if self.binary_name_filter: | 437 if self.binary_name_filter: |
431 binary = self.binary_name_filter(binary) | 438 binary = self.binary_name_filter(binary) |
432 symbolized_line = self.symbolize_address(addr, binary, offset) | 439 symbolized_line = self.symbolize_address(addr, binary, offset) |
433 if not symbolized_line: | 440 if not symbolized_line: |
434 if original_binary != binary: | 441 if original_binary != binary: |
435 symbolized_line = self.symbolize_address(addr, binary, offset) | 442 symbolized_line = self.symbolize_address(addr, binary, offset) |
436 return self.get_symbolized_lines(symbolized_line) | 443 return self.get_symbolized_lines(symbolized_line) |
437 | 444 |
438 | 445 |
439 if __name__ == '__main__': | 446 if __name__ == '__main__': |
440 parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFo
rmatter, | 447 parser = argparse.ArgumentParser( |
441 description='ASan symbolization script', | 448 formatter_class=argparse.RawDescriptionHelpFormatter, |
442 epilog='''Example of use: | 449 description='ASan symbolization script', |
443 asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" -s "$HOME/Symbol
Files" < asan.log''') | 450 epilog='Example of use:\n' |
| 451 'asan_symbolize.py -c "$HOME/opt/cross/bin/arm-linux-gnueabi-" ' |
| 452 '-s "$HOME/SymbolFiles" < asan.log') |
444 parser.add_argument('path_to_cut', nargs='*', | 453 parser.add_argument('path_to_cut', nargs='*', |
445 help='pattern to be cut from the result file path ') | 454 help='pattern to be cut from the result file path ') |
446 parser.add_argument('-d','--demangle', action='store_true', | 455 parser.add_argument('-d','--demangle', action='store_true', |
447 help='demangle function names') | 456 help='demangle function names') |
448 parser.add_argument('-s', metavar='SYSROOT', | 457 parser.add_argument('-s', metavar='SYSROOT', |
449 help='set path to sysroot for sanitized binaries') | 458 help='set path to sysroot for sanitized binaries') |
450 parser.add_argument('-c', metavar='CROSS_COMPILE', | 459 parser.add_argument('-c', metavar='CROSS_COMPILE', |
451 help='set prefix for binutils') | 460 help='set prefix for binutils') |
452 parser.add_argument('-l','--logfile', default=sys.stdin, type=argparse.FileTyp
e('r'), | 461 parser.add_argument('-l','--logfile', default=sys.stdin, |
453 help='set log file name to parse, default is stdin') | 462 type=argparse.FileType('r'), |
| 463 help='set log file name to parse, default is stdin') |
454 args = parser.parse_args() | 464 args = parser.parse_args() |
455 if args.path_to_cut: | 465 if args.path_to_cut: |
456 fix_filename_patterns = args.path_to_cut | 466 fix_filename_patterns = args.path_to_cut |
457 if args.demangle: | 467 if args.demangle: |
458 demangle = True | 468 demangle = True |
459 if args.s: | 469 if args.s: |
460 binary_name_filter = sysroot_path_filter | 470 binary_name_filter = sysroot_path_filter |
461 sysroot_path = args.s | 471 sysroot_path = args.s |
462 if args.c: | 472 if args.c: |
463 binutils_prefix = args.c | 473 binutils_prefix = args.c |
464 if args.logfile: | 474 if args.logfile: |
465 logfile = args.logfile | 475 logfile = args.logfile |
466 else: | 476 else: |
467 logfile = sys.stdin | 477 logfile = sys.stdin |
468 loop = SymbolizationLoop(binary_name_filter) | 478 loop = SymbolizationLoop(binary_name_filter) |
469 loop.process_logfile() | 479 loop.process_logfile() |
OLD | NEW |