如何清空堆栈 android 清空表csdn-CSDN论坛

深入理解Android NDK日志符号化
发表于 18:56|
作者贾志凯
摘要:现在的App基本都会采集上报崩溃时的日志信息,无论是采用第三方云平台,还是自己搭建云服务,都要将含调试信息的so动态库上传,实现云端日志符号化和云端可视化管理,本文对so动态库的组成结构进行了深入分析。
CSDN移动将持续为您优选移动开发的精华内容,共同探讨移动开发的技术热点话题,涵盖移动应用、开发工具、移动游戏及引擎、智能硬件、物联网等方方面面。如果您想投稿、参与内容翻译工作,或寻求近匠报道,请发送邮件至tangxy#csdn.net(请把#改成@)。
为了进行代码及产品保护,几乎所有的非开源App都会进行代码混淆。这样,当收集到崩溃信息后,就需要进行符号化来还原代码信息,以便开发者可以定位Bug。基于使用SDK和NDK的不同,Android的崩溃分为两类:Java崩溃和C/C++崩溃。Java崩溃通过mapping.txt文件进行符号化,比较简单直观。而C/C++崩溃的符号化则需要使用Google自带的一些NDK工具,比如ndk-stack、addr2line、objdump等。本文不去讨论如何使用这些工具,有兴趣的朋友可以参考之前尹春鹏写的另一篇文章《
》,里面做了详细的描述。
基于NDK的Android开发都会生成一个动态链接库(so),它是基于C/C++编译生成的。动态链接库在Linux系统下广泛使用,而Android系统底层是基于Linux的,所以NDK
so库的编译生成遵循相同的规则,只不过Google NDK把相关的交叉编译工具都封装了。
Ndk-build编译时会生成的两个同名的so库,位于不同的目录/project path/libs/armeabi/xxx.so和/project
path/obj/local/armeabi/xxx.so,比较两个so文件会发现体积相差很大。前者会跟随App一起发布,所以尽可能地小,而后者包含了很多调试信息,主要为了gdb调试的时候使用,当然,NDK的日志符号化信息也包含其中。
深入分析so动态库组成结构
本文主要针对这个包含调试信息的so动态库,深入分析它的组成结构。在开始之前,先来说说这样做的目的或者好处。现在的App基本都会采集上报崩溃时的日志信息,无论是采用第三方云平台,还是自己搭建云服务,都要将含调试信息的so动态库上传,实现云端日志符号化以及云端可视化管理。
移动App的快速迭代,使得我们必须存储管理每一个版本的debug so库,而其包含了很多与符号化无关的信息。如果我们只提取出符号化需要的信息,那么符号化文件的体积将会呈现数量级的减少。同时可以在自定义的符号化文件中添加App的版本号等定制化信息,实现符号化提取、上传到云端、云端解析及可视化等自动化部署。另外,从技术角度讲,开发者将不再害怕看到“unresolved
symbol” linking errors,可以更从容地debugging C/C++ crash或进行一些hacking操作。
首先通过readelf来看看两个不同目录下的so库有什么不同。
从中可以清楚看到,包含调试信息的so库多了8个.debug_开头的条目以及.symtab和.strtab条目。符号化的本质,是通过堆栈中的地址信息,还原代码本来的语句以及相应的行号,所以这里只需解析.debug_
line和.symtab,最终获取到如下的信息就可以实现符号化了。
jni/hello-jni.c:27-29
jni/hello-jni.c:32
JNI_OnLoad
jni/hello-jni.c:34
JNI_OnLoad
jni/hello-jni.c:35
JNI_OnLoad
jni/hello-jni.c:37
通常,目标文件分为三类:relocatable文件、executable文件和shared object文件,它们格式称为ELF(Executable
and Linking Format),so动态库属于第三类shared object,它的整体组织结构如下:
ELF Header
ELF Header文件头的结构如下,记录了文件其他内容在文件中的偏移以及大小信息。这里以32bit为例:
typedef struct {
unsigned char
e_ident[EI_NIDENT];
Elf32_Half
// 目标文件类型,如relocatable、executable和shared object
Elf32_Half
// 指定需要的特定架构,如Intel 80386,Motorola 68000
Elf32_Word
// 目标文件版本,通e_ident中的EI_VERSION
Elf32_Addr
// 指定入口点地址,如C可执行文件的入口是_start(),而不是main()
// program header table 的偏移量
// section header table的偏移量
Elf32_Word
// 处理器相关的标志
Elf32_Half
// 代表ELF Header部分的大小
Elf32_Half
e_ // program header table中每一项的大小
Elf32_Half
// program header table包含多少项
Elf32_Half
// section header table中每一项的大小
Elf32_Half
// section header table包含多少项
Elf32_Half
//section header table中某一子项的index,该子项包含了所有section的字符串名称
其中e_ident为固定16个字节大小的数组,称为ELF Identification,包含了处理器类型、文件编码格式、机器类型等,具体结构如下:
该部分包含了除ELF Header、program header table以及section header table之外的所有信息。通过section
header table可以找到每一个section的基本信息,如名称、类型、偏移量等。
先来看看Section Header的内容,仍以32-bit为例:
typedef struct {
Elf32_Word sh_
// 指定section的名称,该值为String Table字符串表中的索引
Elf32_Word sh_ // 指定section的分类
Elf32_Word sh_ // 该字段的bit代表不同的section属性
Elf32_Addr sh_ // 如果section出现在内存镜像中,该字段表示section第一个字节的地址
Elf32_Off sh_ // 指定section在文件中的偏移量
Elf32_Word sh_ // 指定section占用的字节大小
Elf32_Word sh_ // 相关联的section header table的index
Elf32_Word sh_ // 附加信息,意义依赖于section的类型
Elf32_Word sh_ // 指定地址对其约束
Elf32_Word sh_
// 如果section包含一个table,该值指定table中每一个子项的大小
通过Section Header的sh_name可以找到指定的section,比如.debug_line、.symbol、.strtab。
String Table
String Table包含一系列以\0结束的字符序列,最后一个字节设置为\0,表明所有字符序列的结束,比如:
String Table也属于section,只不过它的偏移量直接在ELF Header中的e_shstrndx字段指定。String Table的读取方法是,从指定的index开始,直到遇到休止符。比如要section
header中sh_name获取section的名称,假设sh_name = 7, 则从string table字节流的第7个index开始(注意这里从0开始),一直读到第一个休止符(index=18),读取到的名称为.debug_line。
Symbol Table
该部分包含了程序符号化的定义相关信息,比如函数定义、变量定义等,每一项的定义如下:
# Symbol Table Entry
typedef struct {
Elf32_Word st_
//symbol字符串表的索引
Elf32_Addr st_
//symbol相关的值,依赖于symbol的类型
Elf32_Word st_
//symbol内容的大小
unsigned char st_
//symbol的类型及其属性
unsigned char st_
//symbol的可见性,比如类的public等属性
Elf32_Half st_
//与此symbol相关的section header的索引
Symbol的类型包含以下几种:
其中STT_FUNC就是我们要找的函数symbol。然后通过st_name从symbol字符串表中获取到相应的函数名(如JNI_OnLoad)。当symbol类型为STT_FUNC时,st_value代表该symbol的起始地址,而(st_value+st_size)代表该symbol的结束地址。
回顾之前提到的.symtab和.strtab两个部分,对应的便是Symbol Section和Symbol String Section。
DWARF(Debugging With Attributed Record Formats)
DWARF是一种调试文件格式,很多编译器和调试器都通过它进行源码调试(gdb等)。尽管它是一种独立的目标文件格式,但往往嵌入在ELF文件中。前面通过readelf看到的8个.debug_*
Section全部都属于DWARF格式。本文将只讨论与符号化相关的.debug_line部分,更多的DWARF信息请查看参考文献的内容。
.debug_line部分包含了行号信息,通过它可以将代码语句和机器指令地址对应,从而进行源码调试。.debug_line由很多子项组成,每个子项都包含类似数据块头的描述,称为Statement
Program Prologue。Prologue提供了解码程序指令和跳转到其他语句的信息,它包含如下字段,这些字段是以二进制格式顺序存在的:
这里用到的机器指令可以分为三类:
这里不做机器指令的解析说明,感兴趣的,可以查看参考文献的内容。
通过.debug_line,我们最终可以获得如下信息:文件路径、文件名、行号以及起始地址。
最后,我们汇总一下整个符号化提取的过程:
从ELF Header中获知32bit或者64bit,以及大端还是小端,基于此读取后面的内容;从ELF Header中获得Section Header Table在文件中的位置;读取Section Header Table,从中获得.debug_line、.symtab以及.strtab三个section在文中的位置;读取.symtab和.strtab两个section,最后获得所有function symbol的名称、起始地址以及结束地址;读取.debug_line,按照DWARF格式解析获取文件名称、路径、行号以及起始地址;对比步骤4和5中获取的结果,进行对比合并,形成最终的结果。
作者简介:
贾志凯 Testin技术总监,主要负责崩溃分析项目Android平台架构设计、性能及稳定性优化。
第一时间掌握最新移动开发相关信息和技术,请关注mobilehub公众微信号(ID: mobilehub)。
推荐阅读相关主题:
CSDN官方微信
扫描二维码,向CSDN吐槽
微信号:CSDNnews
相关热门文章如何清空android ListView控件的内容
[问题点数:20分,结帖人lzc_3486138]
如何清空android ListView控件的内容
[问题点数:20分,结帖人lzc_3486138]
不显示删除回复
显示所有回复
显示星级回复
显示得分回复
只显示楼主
2012年10月 移动平台大版内专家分月排行榜第二2012年7月 移动平台大版内专家分月排行榜第二2012年6月 移动平台大版内专家分月排行榜第二2012年5月 移动平台大版内专家分月排行榜第二
本帖子已过去太久远了,不再提供回复功能。12140人阅读
移动云(40)
每次发生android runtime exception后都手足无措,因为拿到的都是如下信息:adb logcat:F/libc
(21559): Fatal signal 11 (SIGSEGV) at 0x (code=1), thread 21578 (m.yunos.browser)
160): *** *** *** *** *** *** *** *** *** *** *** *** *** *** *** ***
160): Build fingerprint: 'Android/full_mako/mako:4.2.1/JOP40G/eng.yuanzhijun.707:userdebug/test-keys'
160): Revision: '11'
160): pid: 21559, tid: 21578, name: m.yunos.browser
&&& com.yunos.browser &&&
160): signal 11 (SIGSEGV), code 1 (SEGV_MAPERR), fault addr
r1 74514f0c
r2 0000001c
r3 74252cf8
160): backtrace:
/system/lib/libwebcore.tgl.so
pc 001b8109
/system/lib/libwebcore.tgl.so
......只能大概知道是libwebcore.tgl.so里发生了错误,如何能打印出详细的堆栈信息呢?起码也得知道是哪个方法抛的错。开始我尝试自己写代码来捕获signal,然后再调用backtrace和backtrace_symbols,但在android的linux上用不了两个方法,估计是得装gcc的扩展库,先把这个方案搁置一下,后面再尝试。然后我再尝试NDK下的ndk-stack,但看语义应该需要NDK编译的才能用,反正我尝试了ndk-stack -sym ...&&-dump ...后,还是失败,原因是symbols找不到,如果是基于ndk开发的android native应用应该是没问题,但这里我是直接要调试android源码,也搁置一下。最后尝试用NDK下的addr2line,这个能用,方法就是:ndk path\toolchains\arm-linux-androideabi-4.4.3\prebuilt\windows-x86_64\bin\arm-linux-androideabi-addr2line.exe -C -f -e Y:\webos_nexus4\out\target\product\mako\symbols\system\lib\libwebcore.tgl.so 其中Y:\webos_nexus4\out\target\product\mako\symbols\system\lib\libwebcore.tgl.so是编译出的symbols,对应的是开头看到的堆栈中的地址信息:I/DEBUG
/system/lib/libwebcore.tgl.so
运行后得到如下结构:WebCore::InspectorInstrumentation::instrumentingAgentsForContext(WebCore::ScriptExecutionContext*)虽然很笨拙,但起码查到了错误发生在哪个方法里。前面说的两个思路应该可行,我后面再尝试一下
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1927674次
积分:16978
积分:16978
排名:第284名
原创:152篇
转载:14篇
评论:887条
(1)(1)(1)(1)(5)(3)(3)(7)(1)(2)(1)(2)(12)(7)(3)(2)(2)(2)(3)(6)(3)(2)(8)(19)(11)(2)(7)(9)(5)(7)(12)(7)(9)如何清空堆栈 android-CSDN论坛_百度知道
如何清空堆栈 android-CSDN论坛
想清就能清堆栈不是你想清,肯定存在堆栈溢出的可能。 根源还是在于你的代码有问题。 无限次的执行一个方法。这本身就是个有问题的设计
知道智能回答机器人
根据知道用户的观点和内容总结出特定问题的答案,为知道用户提供更好的问答体验。
其他类似问题
为您推荐:
等待您来回答
下载知道APP
随时随地咨询
出门在外也不愁如何去阅读Android的崩溃日志(crash log)和堆栈轨迹(stack trace)
如何去阅读Android的崩溃日志(crash log)和堆栈轨迹(stack trace)
[摘要:当 Android解体正在C/C代码的时间平常会发生像上面如许的解体日记(crash log)。经过“adb logcat”或正在/data/tombstones目次上面的文件中能够看到那些疑息。那篇文章首要说明解体日记的布局、]
&&&&&&& 当Android崩溃在C/C++代码的时候通常会产生像下面这样的崩溃日志(crash log)。通过“adb logcat”或者在/data/tombstones目录下面的文件中可以看到这些信息。这篇文章主要解释崩溃日志的结构、如何阅读以及如何使用栈(stack)工具把地址(addresses)转换成符号(symbols)。
日志中的基本项目解释如下。首先是系统属性“ro.build.fingerprint”中的编译(build)信息。
I/DEBUG&& (& 730): *** *** *** *** *** *** *** *** *** *** *** *** *** ***
I/DEBUG&& (& 730): Build fingerprint: 'generic/generic/generic/:1.5/...'
&&&&&&& 然后它展现了进程ID(pid)和线程ID(tid)。知道了PID,我们可以在/proc/&pid&目录下找到它的信息(在崩溃发生之前)。在这个例子中,PID和TID是一样的。但是如果崩溃发生在子线程中,TID跟PID是不同的。在Linux中线程本身也是一个进程(有它自己的task_struct)。和用fork创建子进程不同的是它和父进程共享地址空间和其他数据。
I/DEBUG&& (& 730): pid: 876, tid: 876& &&& /system/bin/mediaserver &&&
&&&&&& 下面展示进程被终止的信号(signal),在这种情况下,它是一个段错误。紧接着是寄存器值。
I/DEBUG&& (& 730): signal 11 (SIGSEGV), fault addr
I/DEBUG&& (& 730):& r0 & r1 & r2 80248f78& r3
I/DEBUG&& (& 730):& r4 80248f78& r5 & r6 80248f78& r7 beaf9974
I/DEBUG&& (& 730):& r8 & r9
I/DEBUG&& (& 730):& ip afd020c8& sp beaf98d8& lr 8021edcd& pc & cpsr a0000030
&&&&&&& 这是函数调用的堆栈轨迹(stack trace)。#00表示栈指针的深度。“pc&addr&”是栈中的PC地址。有时候”lr”链接寄存器包含返回地址代替PC被展现出来。紧接着是包含崩溃代码的文件。如果不把这些地址信息解释成符号,看起来会很不直观。这个可以通过栈工具来解决。
I/DEBUG&& (& 730):&&&&&&&&& #00& pc & /system/lib/libhelixplayer.so
I/DEBUG&& (& 730):&&&&&&&&& #01& pc 0001edca& /system/lib/libhelixplayer.so
I/DEBUG&& (& 730):&&&&&&&&& #02& pc 0001ff0a& /system/lib/libhelixplayer.so
I/DEBUG&& (& 730):&&&&&&&&& #03& pc & /system/lib/libutils.so
I/DEBUG&& (& 730):&&&&&&&&& #04& pc & /system/lib/libmediaplayerservice.so
I/DEBUG&& (& 730):&&&&&&&&& #15& pc b0001516& /system/bin/linker
&&&&下面是当前的栈,带有栈指针地址和代码转储信息(code dump)。每一行包含4个字节(一个机器字),地址是升序的。栈中的字(words)被映射到它所属的内存区域。
I/DEBUG&& (& 730): stack:
I/DEBUG&& (& 730):&&&& beaf18& [heap]
I/DEBUG&& (& 730):& &&&beaf989c& beaf98d0& [stack]
I/DEBUG&& (& 730):&&&& beaf98a0& 0000db28& [heap]
I/DEBUG&& (& 730):&&&& beaf98a4& beaf98f8& [stack]
I/DEBUG&& (& 730):&&&& beaf98b8& 8021cf4d& /system/lib/libhelixplayer.so
I/DEBUG&& (& 730):&&&& beaf98bc& 80248f78
I/DEBUG& &(& 730): #00 beaf98d8& & [heap]
I/DEBUG&& (& 730):&&&& beaf98dc&
I/DEBUG&& (& 730):&&&& beaf98e0& & [heap]
I/DEBUG&& (& 730):&&&& beaf98e4& 8021edcd& /system/lib/libhelixplayer.so
I/DEBUG&& (& 730): #01 beaf98e8& 80248f78
I/DEBUG &&(& 730):&&&& beaf98ec& 80248f78
crash log的分析工具在资源页里面:http://download.csdn.net/detail/wangmxe/4522459
原文链接:/linux:android:crashlog
感谢关注 Ithao123Linux频道,是专门为互联网人打造的学习交流平台,全面满足互联网人工作与学习需求,更多互联网资讯尽在 IThao123!
Laravel是一套简洁、优雅的PHP Web开发框架(PHP Web Framework)。它可以让你从面条一样杂乱的代码中解脱出来;它可以帮你构建一个完美的网络APP,而且每行代码都可以简洁、富于表达力。
Hadoop是一个由Apache基金会所开发的分布式系统基础架构。
用户可以在不了解分布式底层细节的情况下,开发分布式程序。充分利用集群的威力进行高速运算和存储。
Hadoop实现了一个分布式文件系统(Hadoop Distributed File System),简称HDFS。HDFS有高容错性的特点,并且设计用来部署在低廉的(low-cost)硬件上;而且它提供高吞吐量(high throughput)来访问应用程序的数据,适合那些有着超大数据集(large data set)的应用程序。HDFS放宽了(relax)POSIX的要求,可以以流的形式访问(streaming access)文件系统中的数据。
Hadoop的框架最核心的设计就是:HDFS和MapReduce。HDFS为海量的数据提供了存储,则MapReduce为海量的数据提供了计算。
产品设计是互联网产品经理的核心能力,一个好的产品经理一定在产品设计方面有扎实的功底,本专题将从互联网产品设计的几个方面谈谈产品设计
随着国内互联网的发展,产品经理岗位需求大幅增加,在国内,从事产品工作的大部分岗位为产品经理,其实现实中,很多从事产品工作的岗位是不能称为产品经理,主要原因是对产品经理的职责不明确,那产品经理的职责有哪些,本专题将详细介绍产品经理的主要职责
IThao123周刊

我要回帖

更多关于 android 清空表csdn 的文章

 

随机推荐