КАК получить правильный указатель кадра произвольного потока в iOS?

Способ получить указатель кадра

введите здесь описание изображения

В демонстрационном приложении, работающем на устройстве iPhone 5s / Xcode 7, я попытался получить frame pointer из произвольного потока, используя thread_get_state , но всегда приводит к неверному результату:

- (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;, согласно Apple Doc (ARMv6 и ARM64),

Регистр R7 используется в качестве указателя кадра на ARMv6.

а x29 на ARM64

Регистр указателя фрейма (x29) всегда должен адресовать допустимую запись фрейма, хотя некоторые функции, такие как конечные функции или хвостовые вызовы, могут не создавать запись в этом списке. В результате трассировка стека всегда будет иметь смысл, даже без отладочной информации.

_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) не является правильным указателем кадра?

  1. Если параметром является текущий поток, например, mach_thread_self(), fp всегда равен 0, что отличается от __builtin_frame_address(0);

  2. Если параметр не является текущим потоком, например, основным потоком, указатель на предыдущий указатель кадра fp будет NULL в двух или трех кадрах, что слишком мало для обычного списка ссылок кадров стека;

  3. Все еще не текущий поток, я распечатываю адреса стека вызовов, используя backtrace в основном потоке до sleep. Затем в другом потоке я приостанавливаю основной поток и читаю указатель кадра, используя thread_get_state для обхода стека вызовов, два буфера обратной трассировки совершенно разные;

Что меня смущает

Apple Doc говорит Регистр указателя кадра (x29) всегда должен адресовать действительную запись кадра, но я мог прочитать из него значение ZERO.

Кроме того, в ARM Doc указано В во всех вариантах вызова процедуры стандартные регистры r16, r17, r29 и r30 имеют специальные роли. В этих ролях они помечаются как 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 фрейма листового стека произвольного потока, а затем пройтись по стеку вызовов по Previous Pointer Frame 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 содержит то же значение, что и builtinFP. Но КАК сделать это на произвольном потоке?


person Jason Lee    schedule 15.10.2015    source источник


Ответы (1)


Я нашел проблему в конце концов,

- (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 и т. д.

Я неправильно понял макрос MACHINE_THREAD_STATE, который дал мне универсальное значение для разных архитектур.

person Jason Lee    schedule 22.10.2015