def print_samples(module_list, programs, process_names, counters):
    print 'Samples:'
    for program in programs:
        process_name = '?'
        if program.HasField('process_name_id'):
            process_name = process_names[program.process_name_id]
        print indent('%s (%s)' % (program.name, process_name), 1)
        for module in program.modules:
            if module.HasField('load_module_id'):
                module_descr = module_list[module.load_module_id]
                print indent(module_descr.name, 2)
                has_build_id = module_descr.HasField('build_id')
                if has_build_id:
                    print indent('Build ID: %s' % (module_descr.build_id), 3)
                for addr in module.address_samples:
                    # TODO: Stacks vs single samples.
                    addr_rel = addr.address[0]
                    addr_rel_hex = "%x" % addr_rel
                    print indent('%d %s' % (addr.count, addr_rel_hex), 3)
                    if module_descr.name != '[kernel.kallsyms]':
                        if has_build_id:
                            info = symbol.SymbolInformation(
                                module_descr.name, addr_rel_hex)
                            # As-is, only info[0] (inner-most inlined function) is recognized.
                            (source_symbol, source_location,
                             object_symbol_with_offset) = info[0]
                            if object_symbol_with_offset is not None:
                                print indent(object_symbol_with_offset, 4)
                            if source_symbol is not None:
                                for (sym_inlined, loc_inlined, _) in info:
                                    # TODO: Figure out what's going on here:
                                    if sym_inlined is not None:
                                        print indent(sym_inlined, 5)
                                    else:
                                        print indent('???', 5)
                                    if loc_inlined is not None:
                                        print ' %s' % (indent(loc_inlined, 5))
                        elif module_descr.symbol and (
                                addr_rel & 0x8000000000000000 != 0):
                            index = 0xffffffffffffffff - addr_rel
                            source_symbol = module_descr.symbol[index]
                            print indent(source_symbol, 4)
                        counters_key = None
                        if source_symbol is not None:
                            counters_key = (module_descr.name, source_symbol)
                        else:
                            counters_key = (module_descr.name, addr_rel_hex)
                        if counters_key in counters:
                            counters[counters_key] = counters[
                                counters_key] + addr.count
                        else:
                            counters[counters_key] = addr.count
            else:
                print indent('<Missing module>', 2)
Exemple #2
0
  def write(self, data):
    while True:
      match = re.search(TRACE_LINE, data)
      if not match:
        self._output.write(data)
        break

      frame = match.group('frame')
      lib = match.group('lib')
      addr = match.group('addr')

      # TODO(scherkus): Doing a single lookup per line is pretty slow,
      # especially with larger libraries. Consider caching strategies such as:
      # 1) Have Python load the libraries and do symbol lookups instead of
      #    calling out to addr2line each time.
      # 2) Have Python keep multiple addr2line instances open as subprocesses,
      #    piping addresses and reading back symbols as we find them
      # 3) Read ahead the entire stack trace until we find no more, then batch
      #    the symbol lookups.
      #
      # TODO(scherkus): These results are memoized, which could result in
      # incorrect lookups when running this script on long-lived instances
      # (e.g., adb logcat) when doing incremental development. Consider clearing
      # the cache when modification timestamp of libraries change.
      # pylint: disable=no-member
      sym = symbol.SymbolInformation(lib, addr, False)[0][0]

      if not sym:
        post = match.end('addr')
        self._output.write(data[:post])
        data = data[post:]
        continue

      pre = match.start('frame')
      post = match.end('addr')

      self._output.write(data[:pre])
      self._output.write(frame)
      self._output.write(' ')
      self._output.write(sym)

      data = data[post:]
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)
  def ProcessLine(self, line):
    ret = False
    process_header = self.process_info_line.search(line)
    signal_header = self.signal_line.search(line)
    abort_message_header = self.abort_message_line.search(line)
    thread_header = self.thread_line.search(line)
    register_header = self.register_line.search(line)
    abi_header = self.abi_line.search(line)
    revision_header = self.revision_line.search(line)
    dalvik_jni_thread_header = self.dalvik_jni_thread_line.search(line)
    dalvik_native_thread_header = self.dalvik_native_thread_line.search(line)
    if process_header or signal_header or abort_message_header or thread_header or abi_header or \
        register_header or dalvik_jni_thread_header or dalvik_native_thread_header or revision_header:
      ret = True
      if self.trace_lines or self.value_lines:
        self.PrintOutput(self.trace_lines, self.value_lines)
        self.PrintDivider()
        self.trace_lines = []
        self.value_lines = []
        self.last_frame = -1
      if process_header:
        print process_header.group(1)
      if signal_header:
        print signal_header.group(1)
      if abort_message_header:
        print abort_message_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 revision_header:
        print revision_header.group(1)
      if abi_header:
        print abi_header.group(1)
        symbol.ARCH = abi_header.group(2)
        self.UpdateAbiRegexes()
      return ret
    trace_line_dict = self.MatchTraceLine(line)
    if trace_line_dict is not None:
      ret = True
      frame = trace_line_dict["frame"]
      code_addr = trace_line_dict["offset"]
      area = trace_line_dict["dso"]
      so_offset = trace_line_dict["so_offset"]
      symbol_present = trace_line_dict["symbol_present"]
      symbol_name = trace_line_dict["symbol_name"]

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

      if area == "<unknown>" or area == "[heap]" or area == "[stack]":
        self.trace_lines.append((code_addr, "", area))
      else:
        # If this is an apk, it usually means that there is actually
        # a shared so that was loaded directly out of it. In that case,
        # extract the shared library and the name of the shared library.
        lib = None
        if area.endswith(".apk") and so_offset:
          lib_name, lib = self.GetLibFromApk(area, so_offset)
        if not lib:
          lib = area
          lib_name = None

        # 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(lib, code_addr)
        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 lib_name:
              source_location += "(" + lib_name + ")"
          if nest_count > 0:
            nest_count = nest_count - 1
            arrow = "v------>"
            if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
              arrow = "v-------------->"
            self.trace_lines.append((arrow, source_symbol, source_location))
          else:
            if not object_symbol_with_offset:
              object_symbol_with_offset = source_symbol
            self.trace_lines.append((code_addr,
                                object_symbol_with_offset,
                                source_location))
    if self.code_line.match(line):
      # Code lines should be ignored. If this were exluded the 'code around'
      # sections would trigger value_line matches.
      return ret
    if self.value_line.match(line):
      ret = True
      match = self.value_line.match(line)
      (unused_, addr, value, area, symbol_present, symbol_name) = match.groups()
      if area == "<unknown>" or area == "[heap]" or area == "[stack]" or not area:
        self.value_lines.append((addr, value, "", area))
      else:
        info = symbol.SymbolInformation(area, value)
        (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
        self.value_lines.append((addr,
                            value,
                            object_symbol_with_offset,
                            source_location))

    return ret
    def ProcessLine(self, line):
        ret = False
        process_header = self.process_info_line.search(line)
        signal_header = self.signal_line.search(line)
        abort_message_header = self.abort_message_line.search(line)
        thread_header = self.thread_line.search(line)
        register_header = self.register_line.search(line)
        abi_header = self.abi_line.search(line)
        revision_header = self.revision_line.search(line)
        dalvik_jni_thread_header = self.dalvik_jni_thread_line.search(line)
        dalvik_native_thread_header = self.dalvik_native_thread_line.search(
            line)
        if process_header or signal_header or abort_message_header or thread_header or abi_header or \
            register_header or dalvik_jni_thread_header or dalvik_native_thread_header or revision_header:
            ret = True
            if self.trace_lines or self.value_lines:
                self.PrintOutput(self.trace_lines, self.value_lines)
                self.PrintDivider()
                self.trace_lines = []
                self.value_lines = []
                self.last_frame = -1
            if process_header:
                print process_header.group(1)
            if signal_header:
                print signal_header.group(1)
            if abort_message_header:
                print abort_message_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 revision_header:
                print revision_header.group(1)
            if abi_header:
                print abi_header.group(1)
                symbol.ARCH = abi_header.group(2)
                self.UpdateAbiRegexes()
            return ret
        if self.trace_line.match(line):
            ret = True
            match = self.trace_line.match(line)
            (unused_0, frame, unused_1, code_addr, area, symbol_present,
             symbol_name) = match.groups()

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

            if area == "<unknown>" or area == "[heap]" or area == "[stack]":
                self.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)
                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
                        arrow = "v------>"
                        if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
                            arrow = "v-------------->"
                        self.trace_lines.append(
                            (arrow, source_symbol, source_location))
                    else:
                        if not object_symbol_with_offset:
                            object_symbol_with_offset = source_symbol
                        self.trace_lines.append(
                            (code_addr, object_symbol_with_offset,
                             source_location))
        if self.code_line.match(line):
            # Code lines should be ignored. If this were exluded the 'code around'
            # sections would trigger value_line matches.
            return ret
        if self.value_line.match(line):
            ret = True
            match = self.value_line.match(line)
            (unused_, addr, value, area, symbol_present,
             symbol_name) = match.groups()
            if area == "<unknown>" or area == "[heap]" or area == "[stack]" or not area:
                self.value_lines.append((addr, value, "", area))
            else:
                info = symbol.SymbolInformation(area, value)
                (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
                self.value_lines.append(
                    (addr, value, object_symbol_with_offset, source_location))

        return ret
Exemple #6
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)
def ConvertTrace(lines):
    """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]+.*)")
    # 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(
        "(.*)\#([0-9]+)[ \t]+(..)[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \((.*)\))?"
    )  # pylint: disable-msg=C6310
    # 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]{8})[ \t]+([0-9a-f]{8})[ \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]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[a-f0-9]{8}[ \t]*[ \r\n]"
    )  # pylint: disable-msg=C6310

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

    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)
        if process_header or signal_header or register_header or thread_header \
            or dalvik_jni_thread_header or dalvik_native_thread_header:
            if trace_lines or value_lines:
                PrintOutput(trace_lines, value_lines)
                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)
            continue
        if trace_line.match(line):
            match = trace_line.match(line)
            (unused_0, frame, unused_1, code_addr, area, symbol_present,
             symbol_name) = match.groups()

            if frame <= last_frame and (trace_lines or value_lines):
                PrintOutput(trace_lines, value_lines)
                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)
                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
        if value_line.match(line):
            match = value_line.match(line)
            (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)
                (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)
Exemple #8
0
  def ProcessLine(self, line):
    ret = False
    process_header = self.process_info_line.search(line)
    signal_header = self.signal_line.search(line)
    abort_message_header = self.abort_message_line.search(line)
    thread_header = self.thread_line.search(line)
    register_header = self.register_line.search(line)
    revision_header = self.revision_line.search(line)
    dalvik_jni_thread_header = self.dalvik_jni_thread_line.search(line)
    dalvik_native_thread_header = self.dalvik_native_thread_line.search(line)
    unreachable_header = self.unreachable_line.search(line)
    if process_header or signal_header or abort_message_header or thread_header or \
        register_header or dalvik_jni_thread_header or dalvik_native_thread_header or \
        revision_header or unreachable_header:
      ret = True
      if self.trace_lines or self.value_lines:
        self.PrintOutput(self.trace_lines, self.value_lines)
        self.PrintDivider()
        self.trace_lines = []
        self.value_lines = []
        self.last_frame = -1
      if process_header:
        print(process_header.group(1))
      if signal_header:
        print(signal_header.group(1))
      if abort_message_header:
        print(abort_message_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 revision_header:
        print(revision_header.group(1))
      if unreachable_header:
        print(unreachable_header.group(1))
      return True
    trace_line_dict = self.MatchTraceLine(line)
    if trace_line_dict is not None:
      ret = True
      frame = int(trace_line_dict["frame"])
      code_addr = trace_line_dict["offset"]
      area = trace_line_dict["dso"]
      so_offset = trace_line_dict["so_offset"]
      symbol_present = trace_line_dict["symbol_present"]
      symbol_name = trace_line_dict["symbol_name"]

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

      if area == "<unknown>" or area == "[heap]" or area == "[stack]":
        self.trace_lines.append((code_addr, "", area))
      else:
        # If this is an apk, it usually means that there is actually
        # a shared so that was loaded directly out of it. In that case,
        # extract the shared library and the name of the shared library.
        lib = None
        # The format of the map name:
        #   Some.apk!libshared.so
        # or
        #   Some.apk
        if so_offset:
          # If it ends in apk, we are done.
          apk = None
          if area.endswith(".apk"):
            apk = area
          else:
            index = area.rfind(".so!")
            if index != -1:
              # Sometimes we'll see something like:
              #   #01 pc abcd  libart.so!libart.so (offset 0x134000)
              # Remove everything after the ! and zero the offset value.
              area = area[0:index + 3]
              so_offset = 0
            else:
              index = area.rfind(".apk!")
              if index != -1:
                apk = area[0:index + 4]
          if apk:
            lib_name, lib = self.GetLibFromApk(apk, so_offset)
        if not lib:
          lib = area
          lib_name = None

        # When using atest, test paths are different between the out/ directory
        # and device. Apply fixups.
        lib = self.GetLibPath(lib)

        # 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(lib, code_addr)
        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 lib_name:
              source_location += "(" + lib_name + ")"
          if nest_count > 0:
            nest_count = nest_count - 1
            arrow = "v------>"
            if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
              arrow = "v-------------->"
            self.trace_lines.append((arrow, source_symbol, source_location))
          else:
            if not object_symbol_with_offset:
              object_symbol_with_offset = source_symbol
            self.trace_lines.append((code_addr,
                                object_symbol_with_offset,
                                source_location))
    if self.code_line.match(line):
      # Code lines should be ignored. If this were exluded the 'code around'
      # sections would trigger value_line matches.
      return ret
    if self.value_line.match(line):
      ret = True
      match = self.value_line.match(line)
      (unused_, addr, value, area, symbol_present, symbol_name) = match.groups()
      if area == "<unknown>" or area == "[heap]" or area == "[stack]" or not area:
        self.value_lines.append((addr, value, "", area))
      else:
        info = symbol.SymbolInformation(area, value)
        (source_symbol, source_location, object_symbol_with_offset) = info.pop()
        # If there is no information, skip this.
        if source_symbol or source_location or object_symbol_with_offset:
          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
          self.value_lines.append((addr,
                                   value,
                                   object_symbol_with_offset,
                                   source_location))

    return ret