Home | History | Annotate | Download | only in scripts
      1 #!/usr/bin/env python
      2 #
      3 # Copyright (C) 2013 The Android Open Source Project
      4 #
      5 # Licensed under the Apache License, Version 2.0 (the "License");
      6 # you may not use this file except in compliance with the License.
      7 # You may obtain a copy of the License at
      8 #
      9 #      http://www.apache.org/licenses/LICENSE-2.0
     10 #
     11 # Unless required by applicable law or agreed to in writing, software
     12 # distributed under the License is distributed on an "AS IS" BASIS,
     13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14 # See the License for the specific language governing permissions and
     15 # limitations under the License.
     16 
     17 """stack symbolizes native crash dumps."""
     18 
     19 import re
     20 
     21 import symbol
     22 
     23 def PrintTraceLines(trace_lines):
     24   """Print back trace."""
     25   maxlen = max(map(lambda tl: len(tl[1]), trace_lines))
     26   print
     27   print "Stack Trace:"
     28   print "  RELADDR   " + "FUNCTION".ljust(maxlen) + "  FILE:LINE"
     29   for tl in trace_lines:
     30     (addr, symbol_with_offset, location) = tl
     31     print "  %8s  %s  %s" % (addr, symbol_with_offset.ljust(maxlen), location)
     32   return
     33 
     34 
     35 def PrintValueLines(value_lines):
     36   """Print stack data values."""
     37   maxlen = max(map(lambda tl: len(tl[2]), value_lines))
     38   print
     39   print "Stack Data:"
     40   print "  ADDR      VALUE     " + "FUNCTION".ljust(maxlen) + "  FILE:LINE"
     41   for vl in value_lines:
     42     (addr, value, symbol_with_offset, location) = vl
     43     print "  %8s  %8s  %s  %s" % (addr, value, symbol_with_offset.ljust(maxlen), location)
     44   return
     45 
     46 UNKNOWN = "<unknown>"
     47 HEAP = "[heap]"
     48 STACK = "[stack]"
     49 
     50 
     51 def PrintOutput(trace_lines, value_lines):
     52   if trace_lines:
     53     PrintTraceLines(trace_lines)
     54   if value_lines:
     55     PrintValueLines(value_lines)
     56 
     57 def PrintDivider():
     58   print
     59   print "-----------------------------------------------------\n"
     60 
     61 def ConvertTrace(lines):
     62   """Convert strings containing native crash to a stack."""
     63   process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)")
     64   signal_line = re.compile("(signal [0-9]+ \(.*\).*)")
     65   register_line = re.compile("(([ ]*[0-9a-z]{2} [0-9a-f]{8}){4})")
     66   thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-")
     67   dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)")
     68   dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)")
     69   # Note that both trace and value line matching allow for variable amounts of
     70   # whitespace (e.g. \t). This is because the we want to allow for the stack
     71   # tool to operate on AndroidFeedback provided system logs. AndroidFeedback
     72   # strips out double spaces that are found in tombsone files and logcat output.
     73   #
     74   # Examples of matched trace lines include lines from tombstone files like:
     75   #   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so
     76   #   #00  pc 001cf42e  /data/data/com.my.project/lib/libmyproject.so (symbol)
     77   # Or lines from AndroidFeedback crash report system logs like:
     78   #   03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
     79   # Please note the spacing differences.
     80   trace_line = re.compile("(.*)\#([0-9]+)[ \t]+(..)[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \((.*)\))?")  # pylint: disable-msg=C6310
     81   # Examples of matched value lines include:
     82   #   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so
     83   #   bea4170c  8018e4e9  /data/data/com.my.project/lib/libmyproject.so (symbol)
     84   #   03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
     85   # Again, note the spacing differences.
     86   value_line = re.compile("(.*)([0-9a-f]{8})[ \t]+([0-9a-f]{8})[ \t]+([^\r\n \t]*)( \((.*)\))?")
     87   # Lines from 'code around' sections of the output will be matched before
     88   # value lines because otheriwse the 'code around' sections will be confused as
     89   # value lines.
     90   #
     91   # Examples include:
     92   #   801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
     93   #   03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
     94   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
     95 
     96   trace_lines = []
     97   value_lines = []
     98   last_frame = -1
     99 
    100   for ln in lines:
    101     # AndroidFeedback adds zero width spaces into its crash reports. These
    102     # should be removed or the regular expresssions will fail to match.
    103     line = unicode(ln, errors='ignore')
    104     process_header = process_info_line.search(line)
    105     signal_header = signal_line.search(line)
    106     register_header = register_line.search(line)
    107     thread_header = thread_line.search(line)
    108     dalvik_jni_thread_header = dalvik_jni_thread_line.search(line)
    109     dalvik_native_thread_header = dalvik_native_thread_line.search(line)
    110     if process_header or signal_header or register_header or thread_header \
    111         or dalvik_jni_thread_header or dalvik_native_thread_header:
    112       if trace_lines or value_lines:
    113         PrintOutput(trace_lines, value_lines)
    114         PrintDivider()
    115         trace_lines = []
    116         value_lines = []
    117         last_frame = -1
    118       if process_header:
    119         print process_header.group(1)
    120       if signal_header:
    121         print signal_header.group(1)
    122       if register_header:
    123         print register_header.group(1)
    124       if thread_header:
    125         print thread_header.group(1)
    126       if dalvik_jni_thread_header:
    127         print dalvik_jni_thread_header.group(1)
    128       if dalvik_native_thread_header:
    129         print dalvik_native_thread_header.group(1)
    130       continue
    131     if trace_line.match(line):
    132       match = trace_line.match(line)
    133       (unused_0, frame, unused_1,
    134        code_addr, area, symbol_present, symbol_name) = match.groups()
    135 
    136       if frame <= last_frame and (trace_lines or value_lines):
    137         PrintOutput(trace_lines, value_lines)
    138         PrintDivider()
    139         trace_lines = []
    140         value_lines = []
    141       last_frame = frame
    142 
    143       if area == UNKNOWN or area == HEAP or area == STACK:
    144         trace_lines.append((code_addr, "", area))
    145       else:
    146         # If a calls b which further calls c and c is inlined to b, we want to
    147         # display "a -> b -> c" in the stack trace instead of just "a -> c"
    148         info = symbol.SymbolInformation(area, code_addr)
    149         nest_count = len(info) - 1
    150         for (source_symbol, source_location, object_symbol_with_offset) in info:
    151           if not source_symbol:
    152             if symbol_present:
    153               source_symbol = symbol.CallCppFilt(symbol_name)
    154             else:
    155               source_symbol = UNKNOWN
    156           if not source_location:
    157             source_location = area
    158           if nest_count > 0:
    159             nest_count = nest_count - 1
    160             trace_lines.append(("v------>", source_symbol, source_location))
    161           else:
    162             if not object_symbol_with_offset:
    163               object_symbol_with_offset = source_symbol
    164             trace_lines.append((code_addr,
    165                                 object_symbol_with_offset,
    166                                 source_location))
    167     if code_line.match(line):
    168       # Code lines should be ignored. If this were exluded the 'code around'
    169       # sections would trigger value_line matches.
    170       continue;
    171     if value_line.match(line):
    172       match = value_line.match(line)
    173       (unused_, addr, value, area, symbol_present, symbol_name) = match.groups()
    174       if area == UNKNOWN or area == HEAP or area == STACK or not area:
    175         value_lines.append((addr, value, "", area))
    176       else:
    177         info = symbol.SymbolInformation(area, value)
    178         (source_symbol, source_location, object_symbol_with_offset) = info.pop()
    179         if not source_symbol:
    180           if symbol_present:
    181             source_symbol = symbol.CallCppFilt(symbol_name)
    182           else:
    183             source_symbol = UNKNOWN
    184         if not source_location:
    185           source_location = area
    186         if not object_symbol_with_offset:
    187           object_symbol_with_offset = source_symbol
    188         value_lines.append((addr,
    189                             value,
    190                             object_symbol_with_offset,
    191                             source_location))
    192 
    193   PrintOutput(trace_lines, value_lines)
    194 
    195 
    196 # vi: ts=2 sw=2
    197