Ejemplo n.º 1
0
def _Symbolize(asan_input):
    asan_libs = _FindASanLibraries()
    libraries = collections.defaultdict(list)
    asan_lines = []
    for asan_log_line in [a.rstrip() for a in asan_input]:
        m = _ParseAsanLogLine(asan_log_line)
        if m:
            libraries[m['library']].append(m)
        asan_lines.append({'raw_log': asan_log_line, 'parsed': m})

    all_symbols = collections.defaultdict(dict)
    for library, items in libraries.iteritems():
        libname = _TranslateLibPath(library, asan_libs)
        lib_relative_addrs = set([i['rel_address'] for i in items])
        # pylint: disable=no-member
        info_dict = symbol.SymbolInformationForSet(libname, lib_relative_addrs,
                                                   True)
        if info_dict:
            all_symbols[library]['symbols'] = info_dict

    for asan_log_line in asan_lines:
        m = asan_log_line['parsed']
        if not m:
            print asan_log_line['raw_log']
            continue
        if (m['library'] in all_symbols
                and m['rel_address'] in all_symbols[m['library']]['symbols']):
            s = all_symbols[m['library']]['symbols'][m['rel_address']][0]
            print '%s%s %s %s' % (m['prefix'], m['pos'], s[0], s[1])
        else:
            print asan_log_line['raw_log']
Ejemplo n.º 2
0
def _PrintSymbolized(asan_input, arch):
    """Print symbolized logcat output for Asan symbols.

  Args:
    asan_input: list of input lines.
    arch: Target CPU architecture.
  """
    asan_libs = _FindASanLibraries()

    # Maps library -> [ AsanParsedLine... ]
    libraries = collections.defaultdict(list)

    asan_log_lines = []
    for line in asan_input:
        line = line.rstrip()
        parsed = _ParseAsanLogLine(line)
        if parsed:
            libraries[parsed.library].append(parsed)
        asan_log_lines.append(AsanLogLine(raw=line, parsed=parsed))

    # Maps library -> { address -> [(symbol, location, obj_sym_with_offset)...] }
    all_symbols = collections.defaultdict(dict)

    for library, items in libraries.items():
        libname = _TranslateLibPath(library, asan_libs)
        lib_relative_addrs = set(i.rel_address for i in items)
        # pylint: disable=no-member
        symbols_by_library = symbol.SymbolInformationForSet(libname,
                                                            lib_relative_addrs,
                                                            True,
                                                            cpu_arch=arch)
        if symbols_by_library:
            all_symbols[library] = symbols_by_library

    for log_line in asan_log_lines:
        m = log_line.parsed
        if (m and m.library in all_symbols
                and m.rel_address in all_symbols[m.library]):
            # NOTE: all_symbols[lib][address] is a never-emtpy list of tuples.
            # NOTE: The documentation for SymbolInformationForSet() indicates
            # that usually one wants to display the last list item, not the first.
            # The code below takes the first, is this the best choice here?
            s = all_symbols[m.library][m.rel_address][0]
            symbol_name = s[0]
            symbol_location = s[1]
            print('%s%s %s %s @ \'%s\'' % (m.prefix, m.pos, hex(
                m.rel_address), symbol_name, symbol_location))
        else:
            print(log_line.raw)
Ejemplo n.º 3
0
def ConvertTrace(lines, more_info):
    """Convert strings containing native crash to a stack."""
    process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)")
    signal_line = re.compile("(signal [0-9]+ \(.*\).*)")
    register_line = re.compile("(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})")
    thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-")
    dalvik_jni_thread_line = re.compile(
        "(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)")
    dalvik_native_thread_line = re.compile(
        "(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)")

    width = "{8}"
    if symbol.ARCH == "arm64" or symbol.ARCH == "x86_64":
        width = "{16}"

    # Matches LOG(FATAL) lines, like the following example:
    #   [FATAL:source_file.cc(33)] Check failed: !instances_.empty()
    log_fatal_line = re.compile("(\[FATAL\:.*\].*)$")

    # Note that both trace and value line matching allow for variable amounts of
    # whitespace (e.g. \t). This is because the we want to allow for the stack
    # tool to operate on AndroidFeedback provided system logs. AndroidFeedback
    # strips out double spaces that are found in tombsone files and logcat output.
    #
    # Examples of matched trace lines include lines from tombstone files like:
    #   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so
    #   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so (symbol)
    # Or lines from AndroidFeedback crash report system logs like:
    #   03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
    # Please note the spacing differences.
    trace_line = re.compile(
        "(.*)\#(?P<frame>[0-9]+)[ \t]+(..)[ \t]+(0x)?(?P<address>[0-9a-f]{0,16})[ \t]+(?P<lib>[^\r\n \t]*)(?P<symbol_present> \((?P<symbol_name>.*)\))?"
    )  # pylint: disable-msg=C6310

    # Matches lines emitted by src/base/debug/stack_trace_android.cc, like:
    #   #00 0x7324d92d /data/app-lib/org.chromium.native_test-1/libbase.cr.so+0x0006992d
    # This pattern includes the unused named capture groups <symbol_present> and
    # <symbol_name> so that it can interoperate with the |trace_line| regex.
    debug_trace_line = re.compile('(.*)(?P<frame>\#[0-9]+ 0x[0-9a-f]' + width +
                                  ') '
                                  '(?P<lib>[^+]+)\+0x(?P<address>[0-9a-f]' +
                                  width + ')'
                                  '(?P<symbol_present>)(?P<symbol_name>)')

    # Examples of matched value lines include:
    #   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so
    #   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so (symbol)
    #   03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
    # Again, note the spacing differences.
    value_line = re.compile("(.*)([0-9a-f]" + width + ")[ \t]+([0-9a-f]" +
                            width + ")[ \t]+([^\r\n \t]*)( \((.*)\))?")
    # Lines from 'code around' sections of the output will be matched before
    # value lines because otheriwse the 'code around' sections will be confused as
    # value lines.
    #
    # Examples include:
    #   801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
    #   03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
    code_line = re.compile("(.*)[ \t]*[a-f0-9]" + width + "[ \t]*[a-f0-9]" +
                           width + "[ \t]*[a-f0-9]" + width +
                           "[ \t]*[a-f0-9]" + width + "[ \t]*[a-f0-9]" +
                           width +
                           "[ \t]*[ \r\n]")  # pylint: disable-msg=C6310

    trace_lines = []
    value_lines = []
    last_frame = -1

    # It is faster to get symbol information with a single call rather than with
    # separate calls for each line. Since symbol.SymbolInformation caches results,
    # we can extract all the addresses that we will want symbol information for
    # from the log and call symbol.SymbolInformation so that the results are
    # cached in the following lookups.
    code_addresses = {}
    for ln in lines:
        line = unicode(ln, errors='ignore')
        lib, address = None, None

        match = trace_line.match(line) or debug_trace_line.match(line)
        if match:
            address, lib = match.group('address', 'lib')

        match = value_line.match(line)
        if match and not code_line.match(line):
            (_0, _1, address, lib, _2, _3) = match.groups()

        if lib:
            code_addresses.setdefault(lib, set()).add(address)

    for lib in code_addresses:
        symbol.SymbolInformationForSet(symbol.TranslateLibPath(lib),
                                       code_addresses[lib], more_info)

    for ln in lines:
        # AndroidFeedback adds zero width spaces into its crash reports. These
        # should be removed or the regular expresssions will fail to match.
        line = unicode(ln, errors='ignore')
        process_header = process_info_line.search(line)
        signal_header = signal_line.search(line)
        register_header = register_line.search(line)
        thread_header = thread_line.search(line)
        dalvik_jni_thread_header = dalvik_jni_thread_line.search(line)
        dalvik_native_thread_header = dalvik_native_thread_line.search(line)
        log_fatal_header = log_fatal_line.search(line)
        if (process_header or signal_header or register_header or thread_header
                or dalvik_jni_thread_header or dalvik_native_thread_header
                or log_fatal_header):
            if trace_lines or value_lines:
                PrintOutput(trace_lines, value_lines, more_info)
                PrintDivider()
                trace_lines = []
                value_lines = []
                last_frame = -1
            if process_header:
                print process_header.group(1)
            if signal_header:
                print signal_header.group(1)
            if register_header:
                print register_header.group(1)
            if thread_header:
                print thread_header.group(1)
            if dalvik_jni_thread_header:
                print dalvik_jni_thread_header.group(1)
            if dalvik_native_thread_header:
                print dalvik_native_thread_header.group(1)
            if log_fatal_header:
                print log_fatal_header.group(1)
            continue

        match = trace_line.match(line) or debug_trace_line.match(line)
        if match:
            frame, code_addr, area, symbol_present, symbol_name = match.group(
                'frame', 'address', 'lib', 'symbol_present', 'symbol_name')

            if frame <= last_frame and (trace_lines or value_lines):
                PrintOutput(trace_lines, value_lines, more_info)
                PrintDivider()
                trace_lines = []
                value_lines = []
            last_frame = frame

            if area == UNKNOWN or area == HEAP or area == STACK:
                trace_lines.append((code_addr, "", area))
            else:
                # If a calls b which further calls c and c is inlined to b, we want to
                # display "a -> b -> c" in the stack trace instead of just "a -> c"
                info = symbol.SymbolInformation(area, code_addr, more_info)
                nest_count = len(info) - 1
                for (source_symbol, source_location,
                     object_symbol_with_offset) in info:
                    if not source_symbol:
                        if symbol_present:
                            source_symbol = symbol.CallCppFilt(symbol_name)
                        else:
                            source_symbol = UNKNOWN
                    if not source_location:
                        source_location = area
                    if nest_count > 0:
                        nest_count = nest_count - 1
                        trace_lines.append(
                            ("v------>", source_symbol, source_location))
                    else:
                        if not object_symbol_with_offset:
                            object_symbol_with_offset = source_symbol
                        trace_lines.append(
                            (code_addr, object_symbol_with_offset,
                             source_location))
        if code_line.match(line):
            # Code lines should be ignored. If this were exluded the 'code around'
            # sections would trigger value_line matches.
            continue
        match = value_line.match(line)
        if match:
            (unused_, addr, value, area, symbol_present,
             symbol_name) = match.groups()
            if area == UNKNOWN or area == HEAP or area == STACK or not area:
                value_lines.append((addr, value, "", area))
            else:
                info = symbol.SymbolInformation(area, value, more_info)
                (source_symbol, source_location,
                 object_symbol_with_offset) = info.pop()
                if not source_symbol:
                    if symbol_present:
                        source_symbol = symbol.CallCppFilt(symbol_name)
                    else:
                        source_symbol = UNKNOWN
                if not source_location:
                    source_location = area
                if not object_symbol_with_offset:
                    object_symbol_with_offset = source_symbol
                value_lines.append(
                    (addr, value, object_symbol_with_offset, source_location))

    PrintOutput(trace_lines, value_lines, more_info)
Ejemplo n.º 4
0
def ResolveCrashSymbol(lines, more_info):
    """Convert unicode strings which contains native crash to a stack
  """

    trace_lines = []
    value_lines = []
    last_frame = -1
    pid = -1

    # It is faster to get symbol information with a single call rather than with
    # separate calls for each line. Since symbol.SymbolInformation caches results,
    # we can extract all the addresses that we will want symbol information for
    # from the log and call symbol.SymbolInformation so that the results are
    # cached in the following lookups.
    code_addresses = {}

    # Collects all java exception lines, keyed by pid for later output during
    # native crash handling.
    java_stderr_by_pid = {}
    for line in lines:
        lib, address = None, None

        match = _TRACE_LINE.match(line) or _DEBUG_TRACE_LINE.match(line)
        if match:
            address, lib = match.group('address', 'lib')

        match = _VALUE_LINE.match(line)
        if match and not code_line.match(line):
            (_0, _1, address, lib, _2, _3) = match.groups()

        if lib:
            code_addresses.setdefault(lib, set()).add(address)

        java_stderr_match = _JAVA_STDERR_LINE.search(line)
        if java_stderr_match:
            pid, msg = java_stderr_match.groups()
            java_stderr_by_pid.setdefault(pid, []).append(msg)

    for lib in code_addresses:
        symbol.SymbolInformationForSet(symbol.TranslateLibPath(lib),
                                       code_addresses[lib], more_info)

    for line in lines:
        # AndroidFeedback adds zero width spaces into its crash reports. These
        # should be removed or the regular expresssions will fail to match.
        process_header = _PROCESS_INFO_LINE.search(line)
        signal_header = _SIGNAL_LINE.search(line)
        register_header = _REGISTER_LINE.search(line)
        thread_header = _THREAD_LINE.search(line)
        dalvik_jni_thread_header = _DALVIK_JNI_THREAD_LINE.search(line)
        dalvik_native_thread_header = _DALVIK_NATIVE_THREAD_LINE.search(line)
        log_fatal_header = _LOG_FATAL_LINE.search(line)
        if (process_header or signal_header or register_header or thread_header
                or dalvik_jni_thread_header or dalvik_native_thread_header
                or log_fatal_header):
            if trace_lines or value_lines:
                java_lines = []
                if pid != -1 and pid in java_stderr_by_pid:
                    java_lines = java_stderr_by_pid[pid]
                PrintOutput(trace_lines, value_lines, java_lines, more_info)
                PrintDivider()
                trace_lines = []
                value_lines = []
                last_frame = -1
                pid = -1
            if process_header:
                # Track the last reported pid to find java exceptions.
                pid = _PROCESS_INFO_PID.search(
                    process_header.group(1)).group(1)
                print process_header.group(1)
            if signal_header:
                print signal_header.group(1)
            if register_header:
                print register_header.group(1)
            if thread_header:
                print thread_header.group(1)
            if dalvik_jni_thread_header:
                print dalvik_jni_thread_header.group(1)
            if dalvik_native_thread_header:
                print dalvik_native_thread_header.group(1)
            if log_fatal_header:
                print log_fatal_header.group(1)
            continue

        match = _TRACE_LINE.match(line) or _DEBUG_TRACE_LINE.match(line)
        if match:
            frame, code_addr, area, symbol_present, symbol_name = match.group(
                'frame', 'address', 'lib', 'symbol_present', 'symbol_name')
            logging.debug('Found trace line: %s' % line.strip())

            if frame <= last_frame and (trace_lines or value_lines):
                java_lines = []
                if pid != -1 and pid in java_stderr_by_pid:
                    java_lines = java_stderr_by_pid[pid]
                PrintOutput(trace_lines, value_lines, java_lines, more_info)
                PrintDivider()
                trace_lines = []
                value_lines = []
                pid = -1
            last_frame = frame

            if area == UNKNOWN or area == HEAP or area == STACK:
                trace_lines.append((code_addr, '', area))
            else:
                logging.debug('Identified lib: %s' % area)
                # If a calls b which further calls c and c is inlined to b, we want to
                # display "a -> b -> c" in the stack trace instead of just "a -> c"
                info = symbol.SymbolInformation(area, code_addr, more_info)
                logging.debug('symbol information: %s' % info)
                nest_count = len(info) - 1
                for (source_symbol, source_location,
                     object_symbol_with_offset) in info:
                    if not source_symbol:
                        if symbol_present:
                            source_symbol = symbol.CallCppFilt(symbol_name)
                        else:
                            source_symbol = UNKNOWN
                    if not source_location:
                        source_location = area
                    if nest_count > 0:
                        nest_count = nest_count - 1
                        trace_lines.append(
                            ('v------>', source_symbol, source_location))
                    else:
                        if not object_symbol_with_offset:
                            object_symbol_with_offset = source_symbol
                        trace_lines.append(
                            (code_addr, object_symbol_with_offset,
                             source_location))
        match = _VALUE_LINE.match(line)
        if match:
            (unused_, addr, value, area, symbol_present,
             symbol_name) = match.groups()
            if area == UNKNOWN or area == HEAP or area == STACK or not area:
                value_lines.append((addr, value, '', area))
            else:
                info = symbol.SymbolInformation(area, value, more_info)
                (source_symbol, source_location,
                 object_symbol_with_offset) = info.pop()
                if not source_symbol:
                    if symbol_present:
                        source_symbol = symbol.CallCppFilt(symbol_name)
                    else:
                        source_symbol = UNKNOWN
                if not source_location:
                    source_location = area
                if not object_symbol_with_offset:
                    object_symbol_with_offset = source_symbol
                value_lines.append(
                    (addr, value, object_symbol_with_offset, source_location))

    java_lines = []
    if pid != -1 and pid in java_stderr_by_pid:
        java_lines = java_stderr_by_pid[pid]
    PrintOutput(trace_lines, value_lines, java_lines, more_info)