日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

获取iOS任意线程调用堆栈(一)获取任意线程的调用栈地址列表

發布時間:2024/7/23 编程问答 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 获取iOS任意线程调用堆栈(一)获取任意线程的调用栈地址列表 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

轉載自:http://blog.csdn.net/jasonblog/article/details/49909163

如果要獲取當前線程的調用棧,可以直接使用現有API:[NSThread callStackSymbols]。

但是并沒有相關API支持獲取任意線程的調用棧,所以只能自己編碼實現。

1. 基礎結構

一個線程的調用棧是什么樣的呢?

我的理解是應該包含當前線程的執行地址,并且從這個地址可以一級一級回溯到線程的入口地址,這樣就反向構成了一條鏈:線程入口執行某個方法,然后逐級嵌套調用到當前現場。

(圖片來源于維基百科)

如圖所示,每一級的方法調用,都對應了一張活動記錄,也稱為活動幀。也就是說,調用棧是由一張張幀結構組成的,可以稱之為棧幀。

我們可以看到,一張棧幀結構中包含著Return Address,也就是當前活動記錄執行結束后要返回的地址(展開)。

那么,在我們獲取到棧幀后,就可以通過返回地址來進行回溯了。

2. 指令指針和基址指針

我們明確了兩個目標:(1)當前執行的指令,(2)當前棧幀結構。

以x86為例,寄存器用途如下:

SP/ESP/RSP: Stack pointer for top address of the stack. BP/EBP/RBP: Stack base pointer for holding the address of the current stack frame. IP/EIP/RIP: Instruction pointer. Holds the program counter, the current instruction address.

可以看到,我們可以通過指令指針來獲取當前指令地址,以及通過棧基址指針獲取當前棧幀地址。

那么問題來了,我們怎么獲取到相關寄存器呢?

3. 線程執行狀態

考慮到一個線程被掛起時,后續繼續執行需要恢復現場,所以在掛起時相關現場需要被保存起來,比如當前執行到哪條指令了。

那么就要有相關的結構體來為線程保存運行時的狀態,經過一番查閱,得到如下信息:

The function thread_get_state returns the execution state (e.g. the machine registers) of target_thread as specified by flavor.

Function - Return the execution state for a thread.SYNOPSISkern_return_t thread_get_state(thread_act_t target_thread,thread_state_flavor_t flavor,thread_state_t old_state,mach_msg_type_number_t old_state_count); /** THREAD_STATE_FLAVOR_LIST 0* these are the supported flavors*/ #define x86_THREAD_STATE32 1 #define x86_FLOAT_STATE32 2 #define x86_EXCEPTION_STATE32 3 #define x86_THREAD_STATE64 4 #define x86_FLOAT_STATE64 5 #define x86_EXCEPTION_STATE64 6 #define x86_THREAD_STATE 7 #define x86_FLOAT_STATE 8 #define x86_EXCEPTION_STATE 9 #define x86_DEBUG_STATE32 10 #define x86_DEBUG_STATE64 11 #define x86_DEBUG_STATE 12 #define THREAD_STATE_NONE 13 /* 14 and 15 are used for the internal x86_SAVED_STATE flavours */ #define x86_AVX_STATE32 16 #define x86_AVX_STATE64 17 #define x86_AVX_STATE 18

所以我們可以通過這個API搭配相關參數來獲得想要的寄存器信息:

bool jdy_fillThreadStateIntoMachineContext(thread_t thread, _STRUCT_MCONTEXT *machineContext) {mach_msg_type_number_t state_count = x86_THREAD_STATE64_COUNT;kern_return_t kr = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)&machineContext->__ss, &state_count);return (kr == KERN_SUCCESS); }

這里引入了一個結構體叫_STRUCT_MCONTEXT。

4. 不同平臺的寄存器

_STRUCT_MCONTEXT在不同平臺上的結構不同:

x86_64,如iPhone 6模擬器:

_STRUCT_MCONTEXT64 {_STRUCT_X86_EXCEPTION_STATE64 __es;_STRUCT_X86_THREAD_STATE64 __ss;_STRUCT_X86_FLOAT_STATE64 __fs; };_STRUCT_X86_THREAD_STATE64 {__uint64_t __rax;__uint64_t __rbx;__uint64_t __rcx;__uint64_t __rdx;__uint64_t __rdi;__uint64_t __rsi;__uint64_t __rbp;__uint64_t __rsp;__uint64_t __r8;__uint64_t __r9;__uint64_t __r10;__uint64_t __r11;__uint64_t __r12;__uint64_t __r13;__uint64_t __r14;__uint64_t __r15;__uint64_t __rip;__uint64_t __rflags;__uint64_t __cs;__uint64_t __fs;__uint64_t __gs; };

x86_32,如iPhone 4s模擬器:

_STRUCT_MCONTEXT32 {_STRUCT_X86_EXCEPTION_STATE32 __es;_STRUCT_X86_THREAD_STATE32 __ss;_STRUCT_X86_FLOAT_STATE32 __fs; };_STRUCT_X86_THREAD_STATE32 {unsigned int __eax;unsigned int __ebx;unsigned int __ecx;unsigned int __edx;unsigned int __edi;unsigned int __esi;unsigned int __ebp;unsigned int __esp;unsigned int __ss;unsigned int __eflags;unsigned int __eip;unsigned int __cs;unsigned int __ds;unsigned int __es;unsigned int __fs;unsigned int __gs; };

ARM64,如iPhone 5s:

_STRUCT_MCONTEXT64 {_STRUCT_ARM_EXCEPTION_STATE64 __es;_STRUCT_ARM_THREAD_STATE64 __ss;_STRUCT_ARM_NEON_STATE64 __ns; };_STRUCT_ARM_THREAD_STATE64 {__uint64_t __x[29]; /* General purpose registers x0-x28 */__uint64_t __fp; /* Frame pointer x29 */__uint64_t __lr; /* Link register x30 */__uint64_t __sp; /* Stack pointer x31 */__uint64_t __pc; /* Program counter */__uint32_t __cpsr; /* Current program status register */__uint32_t __pad; /* Same size for 32-bit or 64-bit clients */ };

ARMv7/v6,如iPhone 4s:

_STRUCT_MCONTEXT32 {_STRUCT_ARM_EXCEPTION_STATE __es;_STRUCT_ARM_THREAD_STATE __ss;_STRUCT_ARM_VFP_STATE __fs; };_STRUCT_ARM_THREAD_STATE {__uint32_t __r[13]; /* General purpose register r0-r12 */__uint32_t __sp; /* Stack pointer r13 */__uint32_t __lr; /* Link register r14 */__uint32_t __pc; /* Program counter r15 */__uint32_t __cpsr; /* Current program status register */ };

通過了解以上不同平臺的寄存器結構,我們可以編寫出比較通用的回溯功能。

5. 算法實現

/*** 關于棧幀的布局可以參考:* https://en.wikipedia.org/wiki/Call_stack* http://www.cs.cornell.edu/courses/cs412/2008sp/lectures/lec20.pdf* http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64/*/ typedef struct JDYStackFrame {const struct JDYStackFrame* const previous;const uintptr_t returnAddress; } JDYStackFrame;//int jdy_backtraceThread(thread_t thread, uintptr_t *backtraceBuffer, int limit) {if (limit <= 0) return 0;_STRUCT_MCONTEXT mcontext;if (!jdy_fillThreadStateIntoMachineContext(thread, &mcontext)) {return 0;}int i = 0;uintptr_t pc = jdy_programCounterOfMachineContext(&mcontext);backtraceBuffer[i++] = pc;if (i == limit) return i;uintptr_t lr = jdy_linkRegisterOfMachineContext(&mcontext);if (lr != 0) {/* 由于lr保存的也是返回地址,所以在lr有效時,應該會產生重復的地址項 */backtraceBuffer[i++] = lr;if (i == limit) return i;}JDYStackFrame frame = {0};uintptr_t fp = jdy_framePointerOfMachineContext(&mcontext);if (fp == 0 || jdy_copyMemory((void *)fp, &frame, sizeof(frame)) != KERN_SUCCESS) {return i;}while (i < limit) {backtraceBuffer[i++] = frame.returnAddress;if (frame.returnAddress == 0|| frame.previous == NULL|| jdy_copyMemory((void *)frame.previous, &frame, sizeof(frame)) != KERN_SUCCESS) {break;}}return i; }

總結

以上是生活随笔為你收集整理的获取iOS任意线程调用堆栈(一)获取任意线程的调用栈地址列表的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。