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 prefix = "./prebuilts/gcc/" + uname + "/arm/arm-linux-androideabi-4.6/bin/" 144 addr2line_cmd = prefix + "arm-linux-androideabi-addr2line" 145 146 if (not os.path.exists(addr2line_cmd)): 147 try: 148 prefix = os.environ['ANDROID_BUILD_TOP'] + "/prebuilts/gcc/" + \ 149 uname + "/arm/arm-linux-androideabi-4.6/bin/" 150 except: 151 prefix = ""; 152 153 addr2line_cmd = prefix + "arm-linux-androideabi-addr2line" 154 if (not os.path.exists(addr2line_cmd)): 155 print addr2line_cmd + " not found!" 156 sys.exit(1) 157 158 objdump_cmd = prefix + "arm-linux-androideabi-objdump" 159 cppfilt_cmd = prefix + "arm-linux-androideabi-c++filt" 160 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 ############################################################################### 169 def 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 186 if __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 216 sys.stdout.flush() 217 218 # adb itself aborts 219 stream.close() 220