• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
19import re
20
21import symbol
22
23def 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
35def 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
46UNKNOWN = "<unknown>"
47HEAP = "[heap]"
48STACK = "[stack]"
49
50
51def PrintOutput(trace_lines, value_lines):
52  if trace_lines:
53    PrintTraceLines(trace_lines)
54  if value_lines:
55    PrintValueLines(value_lines)
56
57def PrintDivider():
58  print
59  print "-----------------------------------------------------\n"
60
61def 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