blob: 598af85df1b6671383e6ca4158f132d9707cd922 [file] [log] [blame]
Ben Chengab007ef2009-08-11 11:15:26 -07001#!/usr/bin/env python
2
3# Copyright (C) 2009 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
17import os
18import re
19import string
20import sys
21
22###############################################################################
23# match "#00 pc 0003f52e /system/lib/libdvm.so" for example
24###############################################################################
25trace_line = re.compile("(.*)(\#[0-9]+) (..) ([0-9a-f]{8}) ([^\r\n \t]*)")
26
27# returns a list containing the function name and the file/lineno
28def CallAddr2Line(lib, addr):
29 global symbols_dir
30 global addr2line_cmd
31 global cppfilt_cmd
32
33 if lib != "":
34 cmd = addr2line_cmd + \
35 " -f -e " + symbols_dir + lib + " 0x" + addr
36 stream = os.popen(cmd)
37 lines = stream.readlines()
38 list = map(string.strip, lines)
39 else:
40 list = []
41 if list != []:
42 # Name like "move_forward_type<JavaVMOption>" causes troubles
43 mangled_name = re.sub('<', '\<', list[0]);
44 mangled_name = re.sub('>', '\>', mangled_name);
45 cmd = cppfilt_cmd + " " + mangled_name
46 stream = os.popen(cmd)
47 list[0] = stream.readline()
48 stream.close()
49 list = map(string.strip, list)
50 else:
51 list = [ "(unknown)", "(unknown)" ]
52 return list
53
54
55###############################################################################
56# similar to CallAddr2Line, but using objdump to find out the name of the
57# containing function of the specified address
58###############################################################################
59def CallObjdump(lib, addr):
60 global objdump_cmd
61 global symbols_dir
62
63 unknown = "(unknown)"
64 uname = os.uname()[0]
65 if uname == "Darwin":
66 proc = os.uname()[-1]
67 if proc == "i386":
68 uname = "darwin-x86"
69 else:
70 uname = "darwin-ppc"
71 elif uname == "Linux":
72 uname = "linux-x86"
73 if lib != "":
74 next_addr = string.atoi(addr, 16) + 1
75 cmd = objdump_cmd \
76 + " -C -d --start-address=0x" + addr + " --stop-address=" \
77 + str(next_addr) \
78 + " " + symbols_dir + lib
79 stream = os.popen(cmd)
80 lines = stream.readlines()
81 map(string.strip, lines)
82 stream.close()
83 else:
84 return unknown
85
86 # output looks like
87 #
88 # file format elf32-littlearm
89 #
90 # Disassembly of section .text:
91 #
92 # 0000833c <func+0x4>:
93 # 833c: 701a strb r2, [r3, #0]
94 #
95 # we want to extract the "func" part
96 num_lines = len(lines)
97 if num_lines < 2:
98 return unknown
99 func_name = lines[num_lines-2]
100 func_regexp = re.compile("(^.*\<)(.*)(\+.*\>:$)")
101 components = func_regexp.match(func_name)
102 if components is None:
103 return unknown
104 return components.group(2)
105
106###############################################################################
107# determine the symbols directory in the local build
108###############################################################################
109def FindSymbolsDir():
110 global symbols_dir
111
112 try:
113 path = os.environ['ANDROID_PRODUCT_OUT'] + "/symbols"
114 except:
115 cmd = "CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core " \
Ying Wang77f31fe2010-09-03 17:45:46 -0700116 + "SRC_TARGET_DIR=build/target make -f build/core/config.mk " \
Ben Chengab007ef2009-08-11 11:15:26 -0700117 + "dumpvar-abs-TARGET_OUT_UNSTRIPPED"
118 stream = os.popen(cmd)
119 str = stream.read()
120 stream.close()
121 path = str.strip()
122
123 if (not os.path.exists(path)):
124 print path + " not found!"
125 sys.exit(1)
126
127 symbols_dir = path
128
129###############################################################################
130# determine the path of binutils
131###############################################################################
132def SetupToolsPath():
133 global addr2line_cmd
134 global objdump_cmd
135 global cppfilt_cmd
136 global symbols_dir
137
138 uname = os.uname()[0]
139 if uname == "Darwin":
Romain Guyaaf01f42012-03-13 16:56:17 -0700140 uname = "darwin-x86"
Ben Chengab007ef2009-08-11 11:15:26 -0700141 elif uname == "Linux":
142 uname = "linux-x86"
Jing Yuf5172c72012-03-29 20:45:50 -0700143 prefix = "./prebuilts/gcc/" + uname + "/arm/arm-linux-androideabi-4.6/bin/"
Jing Yu9d396e32010-07-29 15:07:31 -0700144 addr2line_cmd = prefix + "arm-linux-androideabi-addr2line"
Ben Chengab007ef2009-08-11 11:15:26 -0700145
146 if (not os.path.exists(addr2line_cmd)):
147 try:
Jing Yuf5172c72012-03-29 20:45:50 -0700148 prefix = os.environ['ANDROID_BUILD_TOP'] + "/prebuilts/gcc/" + \
149 uname + "/arm/arm-linux-androideabi-4.6/bin/"
Ben Chengab007ef2009-08-11 11:15:26 -0700150 except:
151 prefix = "";
152
Jing Yu9d396e32010-07-29 15:07:31 -0700153 addr2line_cmd = prefix + "arm-linux-androideabi-addr2line"
Ben Chengab007ef2009-08-11 11:15:26 -0700154 if (not os.path.exists(addr2line_cmd)):
155 print addr2line_cmd + " not found!"
156 sys.exit(1)
157
Jing Yu9d396e32010-07-29 15:07:31 -0700158 objdump_cmd = prefix + "arm-linux-androideabi-objdump"
159 cppfilt_cmd = prefix + "arm-linux-androideabi-c++filt"
Ben Chengab007ef2009-08-11 11:15:26 -0700160
161###############################################################################
162# look up the function and file/line number for a raw stack trace line
163# groups[0]: log tag
164# groups[1]: stack level
165# groups[2]: "pc"
166# groups[3]: code address
167# groups[4]: library name
168###############################################################################
169def SymbolTranslation(groups):
170 lib_name = groups[4]
171 code_addr = groups[3]
172 caller = CallObjdump(lib_name, code_addr)
173 func_line_pair = CallAddr2Line(lib_name, code_addr)
174
175 # If a callee is inlined to the caller, objdump will see the caller's
176 # address but addr2line will report the callee's address. So the printed
177 # format is desgined to be "caller<-callee file:line"
178 if (func_line_pair[0] != caller):
179 print groups[0] + groups[1] + " " + caller + "<-" + \
180 ' '.join(func_line_pair[:]) + " "
181 else:
182 print groups[0] + groups[1] + " " + ' '.join(func_line_pair[:]) + " "
183
184###############################################################################
185
186if __name__ == '__main__':
187 # pass the options to adb
188 adb_cmd = "adb " + ' '.join(sys.argv[1:])
189
190 # setup addr2line_cmd and objdump_cmd
191 SetupToolsPath()
192
193 # setup the symbols directory
194 FindSymbolsDir()
195
196 # invoke the adb command and filter its output
197 stream = os.popen(adb_cmd)
198 while (True):
199 line = stream.readline()
200
201 # EOF reached
202 if (line == ''):
203 break
204
205 # remove the trailing \n
206 line = line.strip()
207
208 # see if this is a stack trace line
209 match = trace_line.match(line)
210 if (match):
211 groups = match.groups()
212 # translate raw address into symbols
213 SymbolTranslation(groups)
214 else:
215 print line
Andy McFadden9611ceb2009-08-31 13:38:35 -0700216 sys.stdout.flush()
Ben Chengab007ef2009-08-11 11:15:26 -0700217
218 # adb itself aborts
219 stream.close()