如何获得iOS中的任意线程的正确的帧指针?
获取帧指针的方法
在iPhone 5s Device / Xcode 7上运行的Demo App上,我尝试使用thread_get_state
获取任意线程的frame pointer
,但总是导致错误:
- (BOOL)fillThreadState:(thread_t)thread intoMachineContext:(_STRUCT_MCONTEXT *)machineContext { mach_msg_type_number_t state_count = MACHINE_THREAD_STATE_COUNT; kern_return_t kr = thread_get_state(thread, MACHINE_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count); if (kr != KERN_SUCCESS) { char *str = mach_error_string(kr); printf("%s\n", str); return NO; } return YES; }
我读这样的帧指针: uintptr_t fp = machineContext.__ss.__fp;
,根据苹果文件( ARMv6和ARM64 ),
寄存器R7被用作ARMv6上的帧指针
而ARM64上的x29
帧指针寄存器(x29)必须总是寻址一个有效的帧logging,尽pipe一些函数(如叶函数或尾调用)可能会select不在此列表中创build一个条目。 因此,即使没有debugging信息,堆栈跟踪也将始终有意义。
_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 */ };
certificate帧指针的方法是错误的
如何certificatefp( machineContext.__ss.__fp
)不是一个正确的帧指针?
-
如果参数是当前线程mach_thread_self(),则fp始终为0 ,与
__builtin_frame_address(0)
。 -
如果参数不是当前线程,例如主线程,则指向前一帧
fp
帧指针的指针将在两帧或三帧中为空,这对于正常的栈帧链表来说太短了; -
仍然不是当前的线程,我
sleep
之前打印出主线程上使用backtrace
的调用堆栈地址。 然后在另一个线程中,我挂起主线程并使用thread_get_state
读取帧指针来thread_get_state
调用堆栈,两个回溯缓冲区完全不同;
什么使我困惑
Apple Doc说帧指针寄存器(x29)必须总是处理有效的帧logging ,但是我可以从中读取一个ZERO值。
此外, ARM Doc
指出,在过程调用标准的所有变体中,寄存器r16,r17,r29和r30都有特殊的作用。 在这些angular色中,当用于保存地址时,它们被标记为IP0,IP1,FP和LR 。
以下是_STRUCT_MCONTEXT
值的一部分示例:
Printing description of machineContext: (__darwin_mcontext64) machineContext = { __es = (__far = 0, __esr = 0, __exception = 0) __ss = { __x = { [0] = 292057776134 [1] = 6142843584 [2] = 3 [3] = 40 [4] = 624 [5] = 17923 [6] = 0 [7] = 0 [8] = 3968 [9] = 4294966207 [10] = 3603 [11] = 70 [12] = 0 [13] = 33332794515418112 [14] = 0 [15] = 4294967295 [16] = 4294967284 [17] = 18446744073709551585 [18] = 4295035980 [19] = 0 [20] = 0 [21] = 0 [22] = 17923 [23] = 624 [24] = 6142843584 [25] = 3 [26] = 40 [27] = 3 [28] = 0 } __fp = 0 __lr = 6142843568 __sp = 6877072044 __pc = 6142843488 __cpsr = 2582105136 __pad = 1 } __ns = {
我在找什么
我现在正在寻找一种方法来获得任意线程的Frame Pointer
的正确值 。
谢谢你的帮助!
更新1
当然,我想要的是得到任意线程的叶堆栈帧的正确的Frame Pointer
,然后沿着Frame pointer
的Previous Pointer
行进调用堆栈。
在此之前,我已经阅读了这些链接:
获取其他线程的回溯
如何通过iPad应用程序中的所有主动线程循环
从另一个线程打印堆栈跟踪
再次感谢。
更新2
我试过在其他平台上,但结果相同: 错误的帧指针 。
- (uintptr_t)framePointerOfMachineContext:(_STRUCT_MCONTEXT *)machineContext { #if defined (__i386__) return machineContext->__ss.__ebp; #endif #if defined (__x86_64__) return machineContext->__ss.__rbp; #endif #if defined (__arm64__) return machineContext->__ss.__fp; #endif }
framePointerOfMachineContext
返回的值与__builtin_frame_address(0)
。
更新3
受到一位同事的启发,我尝试了内联,并在i386:
uintptr_t ebp = 1; __asm__ ("mov %%ebp, %0;" :"=r"(ebp)); uintptr_t builtinFP = (uintptr_t)__builtin_frame_address(0);
现在ebp
variables对于builtinFP
具有相同的值。 但是如何在任意线程上执行此操作?
我最终发现了这个问题,
- (BOOL)fillThreadState:(thread_t)thread intoMachineContext:(_STRUCT_MCONTEXT *)machineContext { mach_msg_type_number_t state_count = MACHINE_THREAD_STATE_COUNT; kern_return_t kr = thread_get_state(thread, MACHINE_THREAD_STATE, (thread_state_t)&machineContext->__ss, &state_count); if (kr != KERN_SUCCESS) { char *str = mach_error_string(kr); printf("%s\n", str); return NO; } return YES; }
thread_get_state
很好,但参数应该是特定于体系结构的,比如x86_THREAD_STATE32
, x86_THREAD_STATE64
等等。
我误解了macrosMACHINE_THREAD_STATE
,这给了我不同的体系结构的普遍意义。