1 #!/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 17 import os 18 import re 19 import string 20 import sys 21 22 ############################################################################### 23 # match "#00 pc 0003f52e /system/lib/libdvm.so" for example 24 ############################################################################### 25 trace_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 28 def 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 ############################################################################### 59 def 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 ############################################################################### 109 def 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 " \ 116 + "SRC_TARGET_DIR=build/target make -f build/core/config.mk " \ 117 + "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 ############################################################################### 132 def 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": 140 uname = "darwin-x86" 141 elif uname == "Linux": 142 uname = "linux-x86" 143 gcc_version = os.environ["TARGET_GCC_VERSION"] 144 prefix = "./prebuilts/gcc/" + uname + "/arm/arm-linux-androideabi-" + \ 145 gcc_version + "/bin/" 146 addr2line_cmd = prefix + "arm-linux-androideabi-addr2line" 147 148 if (not os.path.exists(addr2line_cmd)): 149 try: 150 prefix = os.environ['ANDROID_BUILD_TOP'] + "/prebuilts/gcc/" + \ 151 uname + "/arm/arm-linux-androideabi-" + gcc_version + "/bin/" 152 except: 153 prefix = ""; 154 155 addr2line_cmd = prefix + "arm-linux-androideabi-addr2line" 156 if (not os.path.exists(addr2line_cmd)): 157 print addr2line_cmd + " not found!" 158 sys.exit(1) 159 160 objdump_cmd = prefix + "arm-linux-androideabi-objdump" 161 cppfilt_cmd = prefix + "arm-linux-androideabi-c++filt" 162 163 ############################################################################### 164 # look up the function and file/line number for a raw stack trace line 165 # groups[0]: log tag 166 # groups[1]: stack level 167 # groups[2]: "pc" 168 # groups[3]: code address 169 # groups[4]: library name 170 ############################################################################### 171 def SymbolTranslation(groups): 172 lib_name = groups[4] 173 code_addr = groups[3] 174 caller = CallObjdump(lib_name, code_addr) 175 func_line_pair = CallAddr2Line(lib_name, code_addr) 176 177 # If a callee is inlined to the caller, objdump will see the caller's 178 # address but addr2line will report the callee's address. So the printed 179 # format is desgined to be "caller<-callee file:line" 180 if (func_line_pair[0] != caller): 181 print groups[0] + groups[1] + " " + caller + "<-" + \ 182 ' '.join(func_line_pair[:]) + " " 183 else: 184 print groups[0] + groups[1] + " " + ' '.join(func_line_pair[:]) + " " 185 186 ############################################################################### 187 188 if __name__ == '__main__': 189 # pass the options to adb 190 adb_cmd = "adb " + ' '.join(sys.argv[1:]) 191 192 # setup addr2line_cmd and objdump_cmd 193 SetupToolsPath() 194 195 # setup the symbols directory 196 FindSymbolsDir() 197 198 # invoke the adb command and filter its output 199 stream = os.popen(adb_cmd) 200 while (True): 201 line = stream.readline() 202 203 # EOF reached 204 if (line == ''): 205 break 206 207 # remove the trailing \n 208 line = line.strip() 209 210 # see if this is a stack trace line 211 match = trace_line.match(line) 212 if (match): 213 groups = match.groups() 214 # translate raw address into symbols 215 SymbolTranslation(groups) 216 else: 217 print line 218 sys.stdout.flush() 219 220 # adb itself aborts 221 stream.close() 222