Android 调试中 addr2line 工具的使用

时间:2022-03-16 20:42:43

我们在解bug的时候经常能碰到一些段错误。下面是我从一个buglog中截取的一个段错误:

//////////////////////////////////////////////////////////////////////////////////////////////////////
08-19 19:08:27.132  2105  2105 I DEBUG   : pid: 134, tid: 2104, name: OMXCallbackDisp  >>> /system/bin/mediaserver <<<
08-19 19:08:27.132  2105  2105 I DEBUG   : signal 11 (SIGSEGV), code 2 (SEGV_ACCERR), fault addr 44c86948
08-19 19:08:27.280  1803  1803 I SurfaceTextureClient: [STC::queueBuffer] (this:0x6162c9f8) fps:21.68, dur:1014.76, max:104.52, min:10.36
08-19 19:08:27.281   130  1018 I BufferQueue: [com.android.gallery3d/com.android.camera.Camera](this:0x41c1f008,api:1) [queue] fps:21.70, dur:1013.89, max:103.33, min:10.27
08-19 19:08:27.293   130   321 I SurfaceTextureClient: [STC::queueBuffer] (this:0x406df2e0) fps:34.70, dur:1037.44, max:75.25, min:14.19
08-19 19:08:27.294   130   321 I BufferQueue: [FramebufferSurface](this:0x406de810,api:1) [release] fps:34.70, dur:1037.44, max:75.23, min:14.19
08-19 19:08:27.294   130   321 I BufferQueue: [FramebufferSurface](this:0x406de810,api:1) [queue] fps:34.70, dur:1037.43, max:75.23, min:14.19
08-19 19:08:27.294   130   321 I SurfaceFlinger: [SurfaceFlinger] fps:34.701393,dur:1037.42,max:75.22,min:14.19
08-19 19:08:27.452   465   538 D MDLOGGER: pMDEngine->m_bTerminate is false.  busy for modem = 0,m_nM2ABufCnt = 8
08-19 19:08:27.452   465   538 D MDLOGGER: thrDetectFilter: Detecting catcher_filter.bin exist or not every 5 seconds!
08-19 19:08:27.462  2105  2105 I DEBUG   :     r0 00000000  r1 00000081  r2 00000001  r3 ffffffe8
08-19 19:08:27.462  2105  2105 I DEBUG   :     r4 46d4de60  r5 42671490  r6 40185d35  r7 00100000
08-19 19:08:27.462  2105  2105 I DEBUG   :     r8 45661240  r9 00000000  sl 40dd0159  fp 46c4d930
08-19 19:08:27.462  2105  2105 I DEBUG   :     ip ffffffea  sp 46d4de38  lr 4009915c  pc 44c86948  cpsr 60000010
08-19 19:08:27.462  2105  2105 I DEBUG   : 
08-19 19:08:27.462  2105  2105 I DEBUG   : backtrace:
08-19 19:08:27.462  2105  2105 I DEBUG   :     #00  pc 00042948  <unknown>
08-19 19:08:27.462  2105  2105 I DEBUG   :     #01  pc 0000d158  /system/lib/libc.so
08-19 19:08:27.462  2105  2105 I DEBUG   :     #02  pc 00032954  <unknown>
08-19 19:08:27.462  2105  2105 I DEBUG   : 
08-19 19:08:27.462  2105  2105 I DEBUG   : stack:
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4ddf8  00000000  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4ddfc  00000020  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de00  00000000  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de04  00000000  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de08  00000000  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de0c  46d4de34  [stack:2104]
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de10  44c86a04  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de14  46d4de60  [stack:2104]
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de18  42671490  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de1c  40185d35  /system/lib/libutils.so
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de20  00100000  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de24  45661240  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de28  00000000  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de2c  40dd0159  /system/lib/libstagefright.so
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de30  df0027ad  
08-19 19:08:27.462  2105  2105 I DEBUG   :          46d4de34  00000000  
08-19 19:08:27.462  2105  2105 I DEBUG   :     #00  46d4de38  46d4de60  [stack:2104]
08-19 19:08:27.463  2105  2105 I DEBUG   :          ........  ........
08-19 19:08:27.463  2105  2105 I DEBUG   :     #01  46d4de38  46d4de60  [stack:2104]
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de3c  46c4d958  
08-19 19:08:27.463  2105  2105 I DEBUG   :     #02  46d4de40  46d4de80  [stack:2104]
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de44  40f01d33  /system/lib/libstagefright_omx.so (android::OMXNodeInstance::onMessage(android::omx_message const&)+50)
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de48  46c4db48  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de4c  46c4db5c  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de50  46d4de80  [stack:2104]
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de54  40f00347  /system/lib/libstagefright_omx.so (android::OMX::CallbackDispatcher::loop()+110)
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de58  00000000  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de5c  46c4db50  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de60  00000000  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de64  00000019  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de68  00000000  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de6c  00000000  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de70  00000002  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de74  45def1ac  /system/lib/libMtkOmxVdec.so
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de78  44c89f38  
08-19 19:08:27.463  2105  2105 I DEBUG   :          46d4de7c  45dedea1  /system/lib/libMtkOmxVdec.so (MtkOmxVdec::DeInitVideoDecodeHW()+84)
//////////////////////////////////////////////////////////////////////////////////////////////////////

有没有看起来很眼熟?

关于段错误的定义,我百科了一下:

所谓的段错误就是指访问的内存超出了系统所给这个程序的内存空间,通常这个值是由gd tr来保存的,他是一个48位的寄存器,其中的32位是保存由它指向的 gdt表,后13位保存相应于gdt的下标,最后3位包括了程序是否在内存中以及程序的在cpu中的运行级别,指向 的gdt是由以64位为一个单位的表,在这张表中就保存着程序运行的代码段以及数据段的起始地址以及与此相应的段限和页面交换还有程序运行级别还有内存粒度等等的信息。【不甚理解】

在 Android 的工程当中,有一部分文件会被编译成 so 动态共享库供系统运行时不同模块链接使用。若调用出错的话,会报出如上面文本中贴出来的段错误。

出了这种错误,我们一般都会通过 add2line 工具解析具体出错代码的位置,能精确到哪个文件,哪一行!再去具体位置的代码查看。

若你去找这个文件的时候却发现没有,不必要大惊小怪,MTK没有提供。在MTK 提供的 Android 的工程代码中,经常会出于一些保密方面的考虑,将一些核心文件编译成 so 库提供给客户。让客户看不到这一部分的代码,但是能够正常使用。诸如此类问题,可以提给MTK客服让其帮忙追查Bug!

1,在MTK的工程中,我们是这样来解析段错误的:

我们将 backtrace 以下的段错误 copy 到 trace.txt中。(trace.txt放到工程根目录下)

然后在linux 环境下面,进到工程根目录,执行如下命令:

./adbs -sout/target/product/bbk82_wet_jb5/symbols/ -l trace.txt>a.txt

将会解析得到 a.txt 文件,该文件中就能追溯到具体哪一个文件哪一行出错了。

trace.txt内容:

08-19 19:08:27.462  2105  2105 I DEBUG   : backtrace:
08-19 19:08:27.462  2105  2105 I DEBUG   :     #00  pc 00042948  <unknown>
08-19 19:08:27.462  2105  2105 I DEBUG   :     #01  pc 0000d158  /system/lib/libc.so
08-19 19:08:27.462  2105  2105 I DEBUG   :     #02  pc 00032954  <unknown>

adbs 是一个脚本文件,它的作用是调用 arm-linux-androideabi-addr2line这个工具来解析 trace.txt 中的内容,并将解析的结果存到一个具体的文件中去,譬如 a.txt

-s 和 -l 是参数

解析出来的结果 a.txt内容:

08-19 19:08:27.462  2105  2105 I DEBUG   : backtrace:

08-19 19:08:27.462  2105  2105 I DEBUG   : backtrace:
08-19 19:08:27.462  2105  2105 I DEBUG   :     #00  pc 00042948  <unknown>

08-19 19:08:27.462  2105  2105 I DEBUG   :     #00 (unknown)  (unknown) 
08-19 19:08:27.462  2105  2105 I DEBUG   :     #01  pc 0000d158  /system/lib/libc.so

08-19 19:08:27.462  2105  2105 I DEBUG   :     #01 __pthread_cond_pulse  /home/compiler/workspace/gphone/MT6582/PD1224CT/ALPS.JB5.MP.V1.6_WET_20130810_trunk_user/bionic/libc/bionic/pthread.c:1689 
08-19 19:08:27.462  2105  2105 I DEBUG   :     #02  pc 00032954  <unknown>

08-19 19:08:27.462  2105  2105 I DEBUG   :     #02 (unknown)  (unknown) 
08-19 19:08:27.462  2105  2105 I DEBUG   :

adbs 内容:

#!/usr/bin/env python

import os
import re
import string
import sys
import getopt

###############################################################################
# match "#00  pc 0003f52e  /system/lib/libdvm.so" for example
###############################################################################
trace_line = re.compile("(.*)(\#[0-9]+)  (..) ([0-9a-f]{8})  ([^\r\n \t]*)")


class Options(object):pass
OPTIONS = Options()
OPTIONS.symbols = ""
OPTIONS.log = ""

# 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 = "sourcecode/MT6575/ALPS.ICS.MP.V2_W_20120504/out/target/product/bbk75_cu_ics/symbols/"
    if (len(OPTIONS.symbols) == 0):
      path = os.environ['ANDROID_PRODUCT_OUT'] + "/symbols"
    else:
      path = OPTIONS.symbols
  except:
    cmd = "CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core " \
      + "SRC_TARGET_DIR=build/target make -f build/core/config.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 = "./prebuilts/gcc/" + uname + "/arm/arm-linux-androideabi-4.6/bin/"
  addr2line_cmd = prefix + "arm-linux-androideabi-addr2line"

  if (not os.path.exists(addr2line_cmd)):
    try:
      prefix = os.environ['ANDROID_BUILD_TOP'] + "/prebuilt/gcc/" + uname + \
               "/arm/arm-linux-androideabi-4.6/bin/"
    except:
      prefix = "";

    addr2line_cmd = prefix + "arm-linux-androideabi-addr2line"
    if (not os.path.exists(addr2line_cmd)):
      print addr2line_cmd + " not found!"
      sys.exit(1)

  objdump_cmd = prefix + "arm-linux-androideabi-objdump"
  cppfilt_cmd = prefix + "arm-linux-androideabi-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[:]) + " "

###############################################################################

COMMON_DOCSTRING = """
 -s (--symbols) <symbols dir>
 -l (--log) <logcat file>

eg:
   adbs -s out/target/product/bbk75_cu_ics/symbols/ -l logcat.txt
 or
  adbs -s out/target/product/bbk75_cu_ics/symbols/ logcat
"""

def Usage():
  print COMMON_DOCSTRING

if __name__ == '__main__':

  try:
    opts, args = getopt.getopt(sys.argv[1:], "hs:l:", 
                  ["help", "symbols=", "log="])
  except getopt.GetOptError:
    Usage()

  for o, a in opts:
    if o in ("-h", "--help"):
       Usage()
       sys.exit()
    elif o in ("-s", "--symbols"):
       OPTIONS.symbols = a
    elif o in ("-l", "--log="):
       OPTIONS.log = a

  # pass the options to adb
  #adb_cmd  = "adb " + ' '.join(sys.argv[1:])
  if (len(OPTIONS.log) == 0):
    adb_cmd = "adb " + ' '.join(args)
  else:
    adb_cmd = "cat " + OPTIONS.log

  # 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()
    print line
    # 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()

通过分析,我们最终找到错误发生于 /home/compiler/workspace/gphone/MT6582/project_name/ALPS.JB5.MP.V1.6_WET_20130810_trunk_user/bionic/libc/bionic/pthread.c:1689

实际上libc.so 出错对我们的帮助不大,需要找更上层的出错的地方。或者通过log去分析app层的错误。

注意:

需要注意的是symbols目录需要与 bug对应的版本号编译出来的那个symbols。直接用本地目录下的 symbols 解析出来的会与trace中的错误不一致!如果没有相应的 symbols 目录,那么我们需要自己重新编译一个软件出来复现问题,再从头来分析该段错误并解析。 symbols 下面的库会多出来一些调试信息。

2,高通平台下解析段错误就很简单了。

同样需要对应出问题版本下编译出来的 symbols目录解压覆盖原来的 out/product/project_name/symbols 目录。通过如下方式来实现解析!

arm-eabi-addr2line-fe ./out-PD1225TMA/target/product/msm8960/symbols/system/lib/libc.so 0000e498

-fe 参数 libc.so 出问题的库, 0000e498 函数地址。

参考资料:

1,【百科段错误】http://baike.baidu.com/view/3994532.htm 

2,【linux 下动态链接库、静态链接库】http://www.metsky.com/archives/604.html