9611ceb460
Works better when the output is piped through "tee" to a file.
223 lines
6.6 KiB
Python
Executable file
223 lines
6.6 KiB
Python
Executable file
#!/usr/bin/env python
|
|
|
|
# Copyright (C) 2009 The Android Open Source Project
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the 'License');
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an 'AS IS' BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
import os
|
|
import re
|
|
import string
|
|
import sys
|
|
|
|
###############################################################################
|
|
# match "#00 pc 0003f52e /system/lib/libdvm.so" for example
|
|
###############################################################################
|
|
trace_line = re.compile("(.*)(\#[0-9]+) (..) ([0-9a-f]{8}) ([^\r\n \t]*)")
|
|
|
|
# returns a list containing the function name and the file/lineno
|
|
def CallAddr2Line(lib, addr):
|
|
global symbols_dir
|
|
global addr2line_cmd
|
|
global cppfilt_cmd
|
|
|
|
if lib != "":
|
|
cmd = addr2line_cmd + \
|
|
" -f -e " + symbols_dir + lib + " 0x" + addr
|
|
stream = os.popen(cmd)
|
|
lines = stream.readlines()
|
|
list = map(string.strip, lines)
|
|
else:
|
|
list = []
|
|
if list != []:
|
|
# Name like "move_forward_type<JavaVMOption>" causes troubles
|
|
mangled_name = re.sub('<', '\<', list[0]);
|
|
mangled_name = re.sub('>', '\>', mangled_name);
|
|
cmd = cppfilt_cmd + " " + mangled_name
|
|
stream = os.popen(cmd)
|
|
list[0] = stream.readline()
|
|
stream.close()
|
|
list = map(string.strip, list)
|
|
else:
|
|
list = [ "(unknown)", "(unknown)" ]
|
|
return list
|
|
|
|
|
|
###############################################################################
|
|
# similar to CallAddr2Line, but using objdump to find out the name of the
|
|
# containing function of the specified address
|
|
###############################################################################
|
|
def CallObjdump(lib, addr):
|
|
global objdump_cmd
|
|
global symbols_dir
|
|
|
|
unknown = "(unknown)"
|
|
uname = os.uname()[0]
|
|
if uname == "Darwin":
|
|
proc = os.uname()[-1]
|
|
if proc == "i386":
|
|
uname = "darwin-x86"
|
|
else:
|
|
uname = "darwin-ppc"
|
|
elif uname == "Linux":
|
|
uname = "linux-x86"
|
|
if lib != "":
|
|
next_addr = string.atoi(addr, 16) + 1
|
|
cmd = objdump_cmd \
|
|
+ " -C -d --start-address=0x" + addr + " --stop-address=" \
|
|
+ str(next_addr) \
|
|
+ " " + symbols_dir + lib
|
|
stream = os.popen(cmd)
|
|
lines = stream.readlines()
|
|
map(string.strip, lines)
|
|
stream.close()
|
|
else:
|
|
return unknown
|
|
|
|
# output looks like
|
|
#
|
|
# file format elf32-littlearm
|
|
#
|
|
# Disassembly of section .text:
|
|
#
|
|
# 0000833c <func+0x4>:
|
|
# 833c: 701a strb r2, [r3, #0]
|
|
#
|
|
# we want to extract the "func" part
|
|
num_lines = len(lines)
|
|
if num_lines < 2:
|
|
return unknown
|
|
func_name = lines[num_lines-2]
|
|
func_regexp = re.compile("(^.*\<)(.*)(\+.*\>:$)")
|
|
components = func_regexp.match(func_name)
|
|
if components is None:
|
|
return unknown
|
|
return components.group(2)
|
|
|
|
###############################################################################
|
|
# determine the symbols directory in the local build
|
|
###############################################################################
|
|
def FindSymbolsDir():
|
|
global symbols_dir
|
|
|
|
try:
|
|
path = os.environ['ANDROID_PRODUCT_OUT'] + "/symbols"
|
|
except:
|
|
cmd = "CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core " \
|
|
+ "SRC_TARGET_DIR=build/target make -f build/core/envsetup.mk " \
|
|
+ "dumpvar-abs-TARGET_OUT_UNSTRIPPED"
|
|
stream = os.popen(cmd)
|
|
str = stream.read()
|
|
stream.close()
|
|
path = str.strip()
|
|
|
|
if (not os.path.exists(path)):
|
|
print path + " not found!"
|
|
sys.exit(1)
|
|
|
|
symbols_dir = path
|
|
|
|
###############################################################################
|
|
# determine the path of binutils
|
|
###############################################################################
|
|
def SetupToolsPath():
|
|
global addr2line_cmd
|
|
global objdump_cmd
|
|
global cppfilt_cmd
|
|
global symbols_dir
|
|
|
|
uname = os.uname()[0]
|
|
if uname == "Darwin":
|
|
proc = os.uname()[-1]
|
|
if proc == "i386":
|
|
uname = "darwin-x86"
|
|
else:
|
|
uname = "darwin-ppc"
|
|
elif uname == "Linux":
|
|
uname = "linux-x86"
|
|
prefix = "./prebuilt/" + uname + "/toolchain/arm-eabi-4.4.0/bin/"
|
|
addr2line_cmd = prefix + "arm-eabi-addr2line"
|
|
|
|
if (not os.path.exists(addr2line_cmd)):
|
|
try:
|
|
prefix = os.environ['ANDROID_BUILD_TOP'] + "/prebuilt/" + uname + \
|
|
"/toolchain/arm-eabi-4.4.0/bin/"
|
|
except:
|
|
prefix = "";
|
|
|
|
addr2line_cmd = prefix + "arm-eabi-addr2line"
|
|
if (not os.path.exists(addr2line_cmd)):
|
|
print addr2line_cmd + " not found!"
|
|
sys.exit(1)
|
|
|
|
objdump_cmd = prefix + "arm-eabi-objdump"
|
|
cppfilt_cmd = prefix + "arm-eabi-c++filt"
|
|
|
|
###############################################################################
|
|
# look up the function and file/line number for a raw stack trace line
|
|
# groups[0]: log tag
|
|
# groups[1]: stack level
|
|
# groups[2]: "pc"
|
|
# groups[3]: code address
|
|
# groups[4]: library name
|
|
###############################################################################
|
|
def SymbolTranslation(groups):
|
|
lib_name = groups[4]
|
|
code_addr = groups[3]
|
|
caller = CallObjdump(lib_name, code_addr)
|
|
func_line_pair = CallAddr2Line(lib_name, code_addr)
|
|
|
|
# If a callee is inlined to the caller, objdump will see the caller's
|
|
# address but addr2line will report the callee's address. So the printed
|
|
# format is desgined to be "caller<-callee file:line"
|
|
if (func_line_pair[0] != caller):
|
|
print groups[0] + groups[1] + " " + caller + "<-" + \
|
|
' '.join(func_line_pair[:]) + " "
|
|
else:
|
|
print groups[0] + groups[1] + " " + ' '.join(func_line_pair[:]) + " "
|
|
|
|
###############################################################################
|
|
|
|
if __name__ == '__main__':
|
|
# pass the options to adb
|
|
adb_cmd = "adb " + ' '.join(sys.argv[1:])
|
|
|
|
# setup addr2line_cmd and objdump_cmd
|
|
SetupToolsPath()
|
|
|
|
# setup the symbols directory
|
|
FindSymbolsDir()
|
|
|
|
# invoke the adb command and filter its output
|
|
stream = os.popen(adb_cmd)
|
|
while (True):
|
|
line = stream.readline()
|
|
|
|
# EOF reached
|
|
if (line == ''):
|
|
break
|
|
|
|
# remove the trailing \n
|
|
line = line.strip()
|
|
|
|
# see if this is a stack trace line
|
|
match = trace_line.match(line)
|
|
if (match):
|
|
groups = match.groups()
|
|
# translate raw address into symbols
|
|
SymbolTranslation(groups)
|
|
else:
|
|
print line
|
|
sys.stdout.flush()
|
|
|
|
# adb itself aborts
|
|
stream.close()
|