失效链接处理 |
Native下如何获取调用栈Q?PDF 下蝲
本站整理下蝲Q?/strong>
链接Q?a target="_blank">https://pan.baidu.com/s/1Swf6BtTejHtkCc8PKoEzMQ
提取码:(x)7d72
相关截图Q?/strong>
![]()
主要内容Q?/strong>
Native下如何获取调用栈Q?/div>
2019-02-02 simsun
Android开发高手课 q入评
讲述Q冯永吉
旉 00:54 大小 854.15K
你好Q我?simsunQ曾在微信从?Android 开发,也是开源爱好者、Rust 语言“?/div>
_?rdquo;。应l文邀(g)P很高兴可以在“高手?rdquo;里和你分享一些编译方面的底层知识?/div>
当我们在调试 Native 崩溃或者在?profiling 的时候是十分依赖 backtrace 的,高质量的
backtrace 可以大大减少我们修复崩溃的时间。但你是否了(jin)解系l是如何生成 backtrace
的呢Q今天我们就来探索一?backtrace 背后的故事?/div>
下面是一个常见的 Native 崩溃。通常崩溃本nq没有Q?backtrace 信息Q可以直接获?/div>
的就是当前寄存器的|但显?backtrace 才是能够帮助我们修复 Bug 的关键?/div>
?1 pid: 4637, tid: 4637, name: crasher >>> crasher <<<
?复制代码
??下蝲APP ?/div>
2 signal 6 (SIGABRT), code -6 (SI_TKILL), fault addr --------
3 Abort message: 'some_file.c:123: some_function: assertion "false" failed'
4 r0 00000000 r1 0000121d r2 00000006 r3 00000008
5 r4 0000121d r5 0000121d r6 ffb44a1c r7 0000010c
6 r8 00000000 r9 00000000 r10 00000000 r11 00000000
7 ip ffb44c20 sp ffb44a08 lr eace2b0b pc eace2b16
8 backtrace:
9 #00 pc 0001cb16 /system/lib/libc.so (abort+57)
10 #01 pc 0001cd8f /system/lib/libc.so (__assert2+22)
11 #02 pc 00001531 /system/bin/crasher (do_action+764)
12 #03 pc 00002301 /system/bin/crasher (main+68)
13 #04 pc 0008a809 /system/lib/libc.so (__libc_init+48)
14 #05 pc 00001097 /system/bin/crasher (_start_main+38)
在阅d面的内容之前Q你可以先给自己 2 分钟旉Q思考一下系l是如何生成
backtrace 的呢Q我们通常把生?backtrace 的过E叫?unwindQunwind 看似和我们^
时开发ƈ没有什么关p,但其实很多功能都是依?unwind 的。D个例子,比如你要l制
火焰图或者是在崩溃发生时得到 backtraceQ都需要依?unwind?/div>
书本中的 unwind
1. 函数调用q程
如果你在大学时期修过汇编原理q门评Q相信你?x)对下面的内容还有印象。下囑ְ是一?/div>
非常标准的函数调用的q程?/div>
首先假设我们处于函数 main() q准备调用函?foo()Q调用方?x)按倒序压入参数。此?/div>
W一个参C(x)在调用栈栈顶?/div>
调用 invoke foo() 伪指令,压入当前寄存?EIP 的值到栈,然后载入函数 foo() 的地址
?EIP?/div>
此时Q由于我们已l更改了(jin) EIP 的|?foo() 的地址Q,相当于我们已l进入了(jin)函数
foo()。在执行一个函C前,~译器都?x)给每个函数写一D序aQprologueQ,q里?/div>
压入旧的 EBP |q赋予当?EBP ?ESP 新的|从而Ş成新的一个函数栈?/div>
下一步就行执行函?foo() 本n的代码了(jin)?/div>
l束执行函数 foo() q准备返回,q里~译器也?x)给每个函数插入一D尾?/div>
QepilogueQ用于恢复调用方?ESP ?EBP 来重Z前函数的栈和恢复寄存器?/div>
执行q回指o(h)QretQ,被调用函数的֣QepilogueQ已l恢复了(jin) EBP ?ESPQ然后我
们可以从被恢复的栈中依次 pop ?EIP、所有的参数以及(qing)被暂存的寄存器的倹{?/div>
dq里Q相信如果没有学q汇~原理的同学肯定?x)有一些懵Q我来解释一下上面提到的?/div>
存器~写的具体含义,上述命名均用了(jin) x86 的命名方式。讲q些是希望你对函数调用有
一个初步的理解Q其中有很多l节在不同体pȝ构、不同编译器上的行ؓ(f)都有所区别Q所?/div>
请你放松?j)情Q跟我一L(fng)l向后看?/div>
EBPQ基址指针寄存器,指向栈的底部?/div>
?ARM 体系l构中,R11QARM codeQ或?R7QThumb codeQvC(jin)
cM的作用。在 ARM64 中,此寄存器?X29?/div>
ESPQ栈指针寄存器,指向栈的栈?Q??ARM 下寄存器?R13?/div>
EIPQ指令寄存器Q存储的?CPU 下次要执行的指o(h)的地址QARM 下ؓ(f)
PCQ寄存器?R15?/div>
2. 恢复调用?/div>
如果我们把上q过E羃?yu),站在更高一层视角去看,所有的函数调用栈都?x)Ş成调用?/div>
Qstack frameQ,每一个中都保存?jin)够的信息可以恢复调用函数的栈帧?br />
|