2019-11-21

IOS获取任何线程调用堆栈

原始标题:iOS获取任何线程的调用堆栈

作者| 凉杰,iOS开发者,喜欢Swift,最近一直在学习编译和c,没有想法或目标。

ARM几个通用寄存器

ARM有15种通用寄存器,但实际上有些通用寄存器有特殊用途。过程调用标准定义了寄存器在过程调用中的特殊用途。

R15:pct程序计数器,也称为程序计数器,指令寄存器保存要执行的下一条指令的存储器地址。

R14: LR链接寄存器,也称为子程序链接寄存器,即连接寄存器LR,它保存最后一个函数调用指令的下一个指令的存储器地址,即返回地址。

r13:sp堆栈指针、堆栈指针、sp寄存器将随时将地址保存在堆栈顶部。

R12: ip进程内调用暂存器可以简单地视为临时存储服务点。

事实上,还有另一个r11是可选的,称为FP或帧指针,我们在某些时候使用它来保存堆栈底部的地址。LR为x30寄存器,FP为arm64中的x29寄存器。

ARM堆栈框架

每个线程都有自己的堆栈空间,线程中会有许多函数调用。每个函数调用都有自己的堆栈框架,堆栈由堆栈框架组成。

以下是ARM的堆栈框架布局:

展开全文

主堆栈帧(Main stack frame)是调用函数的堆栈帧,func1堆栈帧是当前函数(被调用函数)的堆栈帧,堆栈底部在高位地址,堆栈向下增长。图中的FP是堆栈基址,它指向函数的堆栈帧起始地址;SP是函数的堆栈指针,指向堆栈的顶部。ARM按堆栈的顺序非常规则。顺序是当前函数指针、返回指针、堆栈指针、堆栈基础指针、输入参数和指针的数量、局部变量和临时变量。如果函数准备好调用另一个函数,临时变量区域必须在跳转之前首先保存另一个函数的参数。

回溯

从上图中,我们可以看到当前堆栈帧中的FP值存储了前一个堆栈帧的FP地址。获得该函数的FP寄存器后,指示堆栈地址并推出堆栈,即可获得调用函数的LR寄存器值,然后通过dynsym动态链接表找到相应的函数名。

void * * CurrentFramePointer =(void * *)machine ConText。__ss。_ _ framePointer

而(i & ltmaxSymbols) {

空* *前帧指针= *当前帧指针;

如果(!前一帧指针)中断;

堆栈[i] = *(当前帧指针+1);

当前帧指针=前一帧指针;

++

}

线程执行状态

正如我们在上面看到的,如果你得到一个线程的左对齐和右对齐寄存器,你可以执行回溯。你怎么得到它的?

线程是pThread的封装。在基础/线程. swift[1]中,您可以看到用pthread封装线程的详细代码。

不同的操作将设计它们自己的线程模型,因此底层的应用编程接口是不同的,但是POSIX提供的pthread相当于封装底层,以使不同的平台以相同的效果运行。

Unix系统提供的线程获取状态和任务线程等方法在内核线程上运行。每个内核线程由thread_t类型的id唯一标识,pthread由pthread_t类型唯一标识。

内核线程和pthread之间的转换(即thread_t和pthread_t之间的相互转换)很容易,因为pthread是为抽象内核线程而生的。

Unix系统提供的线程获取状态和任务线程等方法在内核线程上运行。每个内核线程由thread_t类型的id唯一标识,pthread由pthread_t类型唯一标识。

内核线程和pthread之间的转换(即thread_t和pthread_t之间的相互转换)很容易,因为pthread是为抽象内核线程而生的。

在_STRUCT_MCONTEXT类型的结构中,存储当前线程的速度和最顶层堆栈帧的速度。不同平台上的_STRUCT_MCONTEXT结构不同,例如:

ARM64,如苹果5s

_STRUCT_MCONTEXT64

{

_ STRUCT _ ARM _ EXCEPTION _ STATE64 _ _ es;

_ STRUCT _ ARM _ THREAD _ STATE64 _ _ ss;

_ STRUCT _ ARM _ NEON _ STATE64 _ _ ns

};

_STRUCT_ARM_THREAD_STATE64

{

_ _ uint 64 _ t _ _ x[29];/*通用寄存器x0-x28 */

_ _ uint64 _ t _ _ fp/*帧指针x29 */

_ _ uint64 _ t _ _ lr/*链接寄存器x30 */

_ _ uint64 _ t _ _ sp/*堆栈指针x31 */

_ _ uint64 _ t _ _ pc/*程序计数器*/

_ _ uint32 _ t _ _ cpsr/*当前程序状态寄存器*/

_ _ uint32 _ t _ _ pad/* 32位或64位客户端大小相同*/

};

使用thread_t和_STRUCT_MCONTEXT,可以通过thread_get_state获得线程的FP和SP。

_STRUCT_MCONTEXT机器上下文;

mach _ msg _ type _ number _ t STATECOunt = THREAD _ STATED _ COUNT;

kern _ return _ t kret = THREAD _ get _ STATE(线程,线程_状态_风味,(线程_状态_ t)& amp;(machineContext。__ss),&。state count);

Dladdr获取地址的符号信息。

那么地址的符号信息可以通过dladdr函数和Dl_info获得

externintdladdr(constvoid*,Dl _ info *);

/*

*由dladdr填写的结构。

*/

publicstructdl_info {

public vard Li _ fname:UnSafePointer & lt。Int8>。!/*共享对象的路径名*/

public vard Li _ fbase:UnsFemmersateRawpointer!/*共享对象的基址*/

public vard Li _ sname:UnSafePointer & lt。Int8>。!/*最近符号的名称*/

public vard Li _ saddr:UnsFemmersateRawpointer!/*最近符号的地址*/

publicinit

public init(DLI _ fname:UnSafePointer & lt。Int8>。!,DLI _ fbase:UnsFemmersateRawpointer!,dli_sname: UnsafePointer<。Int8>。!,DLI _ saddr:UnsFemmersateRawpointer!)

}

快速重命名

OC方法没有问题,因为重组规则相对简单,也就是在符号前加上“_”,但是Swift的命名重组相对复杂,所以命名重组后很难识别方法,如下所示:

$ s15rcbackTracedemo 14 Viewcontroller3 BaryyF

因此,我们需要调用swift_demangle来恢复改革后的符号,因此在恢复原始外观后,如下所示:

RCBacktraceDemo。视图控制器。栏->;

斯威夫特更详细的更名可以在周五的问答中看到。2014-08-08:雨燕名称[2].

参考

[1]苹果/swift-corelibs-Foundation/blob/master/Foundation/thread . swift

[2:www . Mike sh . com/pyblog/星期五-QA-2014-08-15-swift-name-mangling . html

[3][

[4]http://blog . csdn . net/jasonblog/article/details/49909163

[5]http://www . cn blogs . com/chyl 411/p/4579053 . html

[6]https://best swifter . com/call stack/

请注意返回搜狐查看更多信息

负责任的编辑:

  • 显示评论
  • 隐藏评论
相关评论
评论分页: 1
欢迎您、远道而来的朋友、感谢您对微信群红包接龙群的关注和支持。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。