フレームポインタを取得する方法
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 でフレーム ポインタとして使用されます。
一方、ARM64 では x29
フレーム ポインター レジスタ (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 */
};
フレームポインタが間違っていることを証明する方法
machineContext.__ss.__fp
fp ( ) が正しいフレーム ポインタではないことを証明するにはどうすればよいですか?
パラメータが現在のスレッド、たとえば mach_thread_self() の場合、fp は常に 0
__builtin_frame_address(0)
であり、これは;とは異なります。パラメーターが現在のスレッド (メイン スレッドなど) でない場合、前のフレーム ポインターへのポインターは
fp
、2 つまたは 3 つのフレームで NULL になります。これは、スタック フレームの通常のリンク リストには短すぎます。まだ現在のスレッドではありませんが、
backtrace
メイン スレッドの前に使用してコール スタック アドレスを出力しますsleep
。次に、別のスレッドで、メイン スレッドを中断しthread_get_state
、コール スタックをウォークするために使用するフレーム ポインターを読み取ります。2 つのバックトレース バッファーはまったく異なります。
何が私を混乱させますか
Apple Docによると、 The frame pointer register (x29) must always address a valid frame record ですが、そこから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
同僚に触発されて、インライン asm を試し、i386 で作成しました。
uintptr_t ebp = 1;
__asm__ ("mov %%ebp, %0;"
:"=r"(ebp));
uintptr_t builtinFP = (uintptr_t)__builtin_frame_address(0);
ebp
変数は と同じ値を保持するようになりましたbuiltinFP
。しかし、任意のスレッドでこれを行う方法は?