blob: 31b68be18715ebe323502ec6c089b7aa95ed59c5 [file] [log] [blame]
Ben Chengb42dad02013-04-25 15:14:04 -07001#!/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))
Brigid Smith45a46c62014-06-10 17:31:32 -070026 spacing = ""
27 if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
28 spacing = " "
Ben Chengb42dad02013-04-25 15:14:04 -070029 print
30 print "Stack Trace:"
Brigid Smith45a46c62014-06-10 17:31:32 -070031 print " RELADDR " + spacing + "FUNCTION".ljust(maxlen) + " FILE:LINE"
Ben Chengb42dad02013-04-25 15:14:04 -070032 for tl in trace_lines:
33 (addr, symbol_with_offset, location) = tl
34 print " %8s %s %s" % (addr, symbol_with_offset.ljust(maxlen), location)
35 return
36
37
38def PrintValueLines(value_lines):
39 """Print stack data values."""
40 maxlen = max(map(lambda tl: len(tl[2]), value_lines))
41 print
42 print "Stack Data:"
43 print " ADDR VALUE " + "FUNCTION".ljust(maxlen) + " FILE:LINE"
44 for vl in value_lines:
45 (addr, value, symbol_with_offset, location) = vl
46 print " %8s %8s %s %s" % (addr, value, symbol_with_offset.ljust(maxlen), location)
47 return
48
49UNKNOWN = "<unknown>"
50HEAP = "[heap]"
51STACK = "[stack]"
52
53
54def PrintOutput(trace_lines, value_lines):
55 if trace_lines:
56 PrintTraceLines(trace_lines)
57 if value_lines:
58 PrintValueLines(value_lines)
59
60def PrintDivider():
61 print
62 print "-----------------------------------------------------\n"
63
Brigid Smith45a46c62014-06-10 17:31:32 -070064def CleanLine(ln):
65 # AndroidFeedback adds zero width spaces into its crash reports. These
66 # should be removed or the regular expresssions will fail to match.
67 return unicode(ln, errors='ignore')
68
Ben Chengb42dad02013-04-25 15:14:04 -070069def ConvertTrace(lines):
70 """Convert strings containing native crash to a stack."""
Brigid Smith45a46c62014-06-10 17:31:32 -070071 lines = map(CleanLine, lines)
72
Ben Chengb42dad02013-04-25 15:14:04 -070073 process_info_line = re.compile("(pid: [0-9]+, tid: [0-9]+.*)")
Brigid Smith45a46c62014-06-10 17:31:32 -070074 abi_line = re.compile("(ABI: \'(.*)\')")
Ben Chengb42dad02013-04-25 15:14:04 -070075 signal_line = re.compile("(signal [0-9]+ \(.*\).*)")
Elliott Hughesd2471c82014-06-17 16:55:10 -070076 abort_message_line = re.compile("(Abort message: '.*')")
Ben Chengb42dad02013-04-25 15:14:04 -070077 thread_line = re.compile("(.*)(\-\-\- ){15}\-\-\-")
78 dalvik_jni_thread_line = re.compile("(\".*\" prio=[0-9]+ tid=[0-9]+ NATIVE.*)")
79 dalvik_native_thread_line = re.compile("(\".*\" sysTid=[0-9]+ nice=[0-9]+.*)")
Narayan Kamath96497002014-04-12 12:16:02 +010080
Brigid Smith45a46c62014-06-10 17:31:32 -070081 for line in lines:
82 abi_header = abi_line.search(line)
83 if abi_header:
84 symbol.ARCH = abi_header.group(2)
85 break
86
Narayan Kamath96497002014-04-12 12:16:02 +010087 width = "{8}"
Brigid Smith45a46c62014-06-10 17:31:32 -070088 if symbol.ARCH == "arm64" or symbol.ARCH == "mips64" or symbol.ARCH == "x86_64":
Narayan Kamath96497002014-04-12 12:16:02 +010089 width = "{16}"
90
Brigid Smith45a46c62014-06-10 17:31:32 -070091 register_line = re.compile("(([ ]*[0-9a-z]{2} +[0-9a-f]" + width + "){4})")
92
Ben Chengb42dad02013-04-25 15:14:04 -070093 # Note that both trace and value line matching allow for variable amounts of
94 # whitespace (e.g. \t). This is because the we want to allow for the stack
95 # tool to operate on AndroidFeedback provided system logs. AndroidFeedback
96 # strips out double spaces that are found in tombsone files and logcat output.
97 #
98 # Examples of matched trace lines include lines from tombstone files like:
99 # #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
100 # #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so (symbol)
101 # Or lines from AndroidFeedback crash report system logs like:
102 # 03-25 00:51:05.520 I/DEBUG ( 65): #00 pc 001cf42e /data/data/com.my.project/lib/libmyproject.so
103 # Please note the spacing differences.
Narayan Kamath96497002014-04-12 12:16:02 +0100104 trace_line = re.compile("(.*)\#([0-9]+)[ \t]+(..)[ \t]+([0-9a-f]" + width + ")[ \t]+([^\r\n \t]*)( \((.*)\))?") # pylint: disable-msg=C6310
Ben Chengb42dad02013-04-25 15:14:04 -0700105 # Examples of matched value lines include:
106 # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
107 # bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so (symbol)
108 # 03-25 00:51:05.530 I/DEBUG ( 65): bea4170c 8018e4e9 /data/data/com.my.project/lib/libmyproject.so
109 # Again, note the spacing differences.
Narayan Kamath96497002014-04-12 12:16:02 +0100110 value_line = re.compile("(.*)([0-9a-f]" + width + ")[ \t]+([0-9a-f]" + width + ")[ \t]+([^\r\n \t]*)( \((.*)\))?")
Ben Chengb42dad02013-04-25 15:14:04 -0700111 # Lines from 'code around' sections of the output will be matched before
112 # value lines because otheriwse the 'code around' sections will be confused as
113 # value lines.
114 #
115 # Examples include:
116 # 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
117 # 03-25 00:51:05.530 I/DEBUG ( 65): 801cf40c ffffc4cc 00b2f2c5 00b2f1c7 00c1e1a8
Narayan Kamath96497002014-04-12 12:16:02 +0100118 code_line = re.compile("(.*)[ \t]*[a-f0-9]" + width +
119 "[ \t]*[a-f0-9]" + width +
120 "[ \t]*[a-f0-9]" + width +
121 "[ \t]*[a-f0-9]" + width +
122 "[ \t]*[a-f0-9]" + width +
123 "[ \t]*[ \r\n]") # pylint: disable-msg=C6310
Ben Chengb42dad02013-04-25 15:14:04 -0700124
125 trace_lines = []
126 value_lines = []
127 last_frame = -1
128
Brigid Smith45a46c62014-06-10 17:31:32 -0700129 for line in lines:
Ben Chengb42dad02013-04-25 15:14:04 -0700130 process_header = process_info_line.search(line)
131 signal_header = signal_line.search(line)
Elliott Hughesd2471c82014-06-17 16:55:10 -0700132 abort_message_header = abort_message_line.search(line)
Ben Chengb42dad02013-04-25 15:14:04 -0700133 thread_header = thread_line.search(line)
Brigid Smith45a46c62014-06-10 17:31:32 -0700134 register_header = register_line.search(line)
135 abi_header = abi_line.search(line)
Ben Chengb42dad02013-04-25 15:14:04 -0700136 dalvik_jni_thread_header = dalvik_jni_thread_line.search(line)
137 dalvik_native_thread_header = dalvik_native_thread_line.search(line)
Elliott Hughesd2471c82014-06-17 16:55:10 -0700138 if process_header or signal_header or abort_message_header or thread_header or abi_header or \
139 register_header or dalvik_jni_thread_header or dalvik_native_thread_header:
Ben Chengb42dad02013-04-25 15:14:04 -0700140 if trace_lines or value_lines:
141 PrintOutput(trace_lines, value_lines)
142 PrintDivider()
143 trace_lines = []
144 value_lines = []
145 last_frame = -1
146 if process_header:
147 print process_header.group(1)
148 if signal_header:
149 print signal_header.group(1)
Elliott Hughesd2471c82014-06-17 16:55:10 -0700150 if abort_message_header:
151 print abort_message_header.group(1)
Ben Chengb42dad02013-04-25 15:14:04 -0700152 if register_header:
153 print register_header.group(1)
154 if thread_header:
155 print thread_header.group(1)
156 if dalvik_jni_thread_header:
157 print dalvik_jni_thread_header.group(1)
158 if dalvik_native_thread_header:
159 print dalvik_native_thread_header.group(1)
Brigid Smith45a46c62014-06-10 17:31:32 -0700160 if abi_header:
161 print abi_header.group(1)
Ben Chengb42dad02013-04-25 15:14:04 -0700162 continue
163 if trace_line.match(line):
164 match = trace_line.match(line)
165 (unused_0, frame, unused_1,
166 code_addr, area, symbol_present, symbol_name) = match.groups()
167
168 if frame <= last_frame and (trace_lines or value_lines):
169 PrintOutput(trace_lines, value_lines)
170 PrintDivider()
171 trace_lines = []
172 value_lines = []
173 last_frame = frame
174
175 if area == UNKNOWN or area == HEAP or area == STACK:
176 trace_lines.append((code_addr, "", area))
177 else:
178 # If a calls b which further calls c and c is inlined to b, we want to
179 # display "a -> b -> c" in the stack trace instead of just "a -> c"
180 info = symbol.SymbolInformation(area, code_addr)
181 nest_count = len(info) - 1
182 for (source_symbol, source_location, object_symbol_with_offset) in info:
183 if not source_symbol:
184 if symbol_present:
185 source_symbol = symbol.CallCppFilt(symbol_name)
186 else:
187 source_symbol = UNKNOWN
188 if not source_location:
189 source_location = area
190 if nest_count > 0:
191 nest_count = nest_count - 1
192 trace_lines.append(("v------>", source_symbol, source_location))
193 else:
194 if not object_symbol_with_offset:
195 object_symbol_with_offset = source_symbol
196 trace_lines.append((code_addr,
197 object_symbol_with_offset,
198 source_location))
199 if code_line.match(line):
200 # Code lines should be ignored. If this were exluded the 'code around'
201 # sections would trigger value_line matches.
202 continue;
203 if value_line.match(line):
204 match = value_line.match(line)
205 (unused_, addr, value, area, symbol_present, symbol_name) = match.groups()
206 if area == UNKNOWN or area == HEAP or area == STACK or not area:
207 value_lines.append((addr, value, "", area))
208 else:
209 info = symbol.SymbolInformation(area, value)
210 (source_symbol, source_location, object_symbol_with_offset) = info.pop()
211 if not source_symbol:
212 if symbol_present:
213 source_symbol = symbol.CallCppFilt(symbol_name)
214 else:
215 source_symbol = UNKNOWN
216 if not source_location:
217 source_location = area
218 if not object_symbol_with_offset:
219 object_symbol_with_offset = source_symbol
220 value_lines.append((addr,
221 value,
222 object_symbol_with_offset,
223 source_location))
224
225 PrintOutput(trace_lines, value_lines)
226
227
228# vi: ts=2 sw=2