如何获得iOS中任意线程的正确帧指针? [英] HOWTO get the correct Frame Pointer of an arbitrary thread in iOS?

查看:103
本文介绍了如何获得iOS中任意线程的正确帧指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在运行于iPhone 5s Device/Xcode 7的演示应用程序上,我尝试使用

On a Demo App running on iPhone 5s Device / Xcode 7, I tried to get the frame pointer of an arbitrary thread using thread_get_state , but always result in an incorrect one :

- (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;
}

根据Apple Doc(

I read the frame pointer like this: uintptr_t fp = machineContext.__ss.__fp;, according to Apple Doc (ARMv6 and ARM64),

寄存器R7用作ARMv6上的帧指针

Register R7 is used as a frame pointer on ARMv6

在ARM64上为x29时

while x29 on ARM64

帧指针寄存器(x29)必须始终寻址有效的帧记录,尽管某些功能(例如叶函数或尾部调用)可能选择不在此列表中创建条目.结果,即使没有调试信息,堆栈跟踪也将始终有意义.

The frame pointer register (x29) must always address a valid frame record, although some functions—such as leaf functions or tail calls—may elect not to create an entry in this list. As a result, stack traces will always be meaningful, even without debug information.

_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 */
};

如何证明帧指针是错误的

如何证明fp(machineContext.__ss.__fp)不是正确的帧指针?

Way to prove the Frame Pointer is wrong

How do I prove that the fp (machineContext.__ss.__fp) is not a correct frame pointer?

  1. 如果参数是当前线程,例如mach_thread_self(),则 fp始终为0 ,与__builtin_frame_address(0);

如果参数不是当前线程(例如主线程),则指向fp的前一帧指针的指针在两三个帧中将为NULL,这对于常规链接列表而言太短了堆栈帧数;

If the parameter is not the current thread, main thread for example, the pointer to the previous frame pointer of fp will be NULL in two or three frames, which is too short for a normal link-list of stack frames;

仍然不是当前线程,我在sleep之前在主线程上使用backtrace打印出调用堆栈地址.然后在另一个线程上,我挂起主线程,并使用thread_get_state读取帧指针以遍历调用堆栈,这两个回溯缓冲区完全不同;

Still not the current thread, I print out the call stack addresses using backtrace on main thread before sleep. Then on another thread, I suspend the main thread and read the frame pointer using thread_get_state to walk the call stack, the two backtrace buffers are totally different;

什么让我感到困惑

What confuses me

Apple Doc says The frame pointer register (x29) must always address a valid frame record, but I could read a ZERO value from it.

此外, ARM Doc 状态在过程调用标准的所有变体中,寄存器r16,r17,r29和r30都具有特殊作用.在这些角色中,他们 在用于保存地址时被标记为IP0,IP1,FP和LR .

Furthermore, ARM Doc states In all variants of the procedure call standard, registers r16, r17, r29 and r30 have special roles. In these roles they are labeled IP0, IP1, FP and LR when being used for holding addresses.

以下是_STRUCT_MCONTEXT值的一部分的示例:

Below is an example of part of _STRUCT_MCONTEXT's value:

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的正确值.

What I am looking for

I'm now searching for a method to get the correct value of the Frame Pointer of an arbitrary thread.

感谢您的帮助!

当然,我想要的是获取任意线程的叶堆栈框架的正确Frame Pointer,然后沿着Frame pointerPrevious Pointer遍历调用堆栈.

Of course, What I want is to get the correct Frame Pointer of the leaf stack frame of an arbitrary thread, and then walk the call stack along the Previous Pointer of the Frame pointer.

在此之前,我已阅读以下链接:

Before this, I've read these links:

获取其他线程的回溯

如何循环遍历iPad中的所有活动线程应用

从另一个线程打印堆栈跟踪

再次感谢.

我在其他平台上尝试过,但结果相同:错误的帧指针.

I've tried on other platforms, but yet the same result : wrong frame pointer.

- (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)不同.

受同事的启发,我尝试了内联汇编并将其安装到i386上:

Inspired by a colleague, I tried inline asm and made it on i386:

uintptr_t ebp = 1;
__asm__ ("mov %%ebp, %0;"
         :"=r"(ebp));

uintptr_t builtinFP = (uintptr_t)__builtin_frame_address(0);

现在,ebp变量的值与builtinFP相同.但是,如何在任意线程上执行此操作?

Now ebp variable holds the same value to builtinFP's. But HOWTO do this on an arbitrary thread?

推荐答案

我最终发现了问题,

- (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_STATE32x86_THREAD_STATE64等.

thread_get_state is fine, but the parameter should be architecture-specific, such as x86_THREAD_STATE32, x86_THREAD_STATE64 and etc.

我误解了宏MACHINE_THREAD_STATE,它赋予了我不同体系结构的通用含义.

I misunderstood the macro MACHINE_THREAD_STATE, which gave me an universal meaning for different architectures.

这篇关于如何获得iOS中任意线程的正确帧指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆