blob: 8277790ba1f55d16b4f91e816712c9ac86912247 [file] [log] [blame]
#!/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.3/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.3/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()