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