backtrace

で、そういう理由でこう、何度目かの backtrace 実装とかしてたんですが、 glibc の backtrace って DWARF2 使う実装とかあるんだなーと気付いた。 GCC ならこれ使うのが一番楽だし、 -fomit-frame-pointer ついてても大丈夫とか偉い。

    // They are in libgcc (see glibc-2.7/sysdeps/ia64/backtrace.c)
    extern(C) int _Unwind_Backtrace(void* fn, void* a);
    extern(C) void* _Unwind_GetIP(void* ctx);

    struct Arg {
        void** buffer;
        int size;
        int cnt;
    }

    int backtrace_helper(void* ctx, void* data) {
        Arg* arg = cast(Arg*)data;
        if (arg.cnt != -1) {
            arg.buffer[arg.cnt] = _Unwind_GetIP(ctx);
        }
        if (++arg.cnt == arg.size) {
            return 5;
        }
        else {
            return 0;
        }
    }

    extern(C) int backtrace(void** buffer, int size) {
        if (size <= 0) return 0;
        Arg arg;
        arg.buffer = buffer;
        arg.size = size;
        arg.cnt = -1;
        _Unwind_Backtrace(&backtrace_helper, &arg);
        return arg.cnt;
    }

というわけで GCC 環境ならこれ使って、 Win32 では StackWalk なり x86x86_64 だけ頑張るなりすれば、メジャーどころな環境はだいたいおさえれそうなのかなぁと思いました。

あと x86_64 でスタックフレームがある場合の backtrace 関数とか書いたんだけど、 GDCインラインアセンブラが 64bit 命令とかレジスタとか対応してないという問題でアセンブリでしょぼしょぼ書いたのだった。そしてその直後で上記を見つけて完全に意味の無い物体になってしまったのでここに意味もなく貼って供養。なむー

.globl backtrace
backtrace:
        push %rbp
        mov %rsp, %rbp
        push %rdi
        push %rsi

        mov %rbp, %rdx
loop:
        cmp $0, %rsi
        je done

        mov 8(%rdx), %rax
        mov %rax, (%rdi)

        mov (%rdx), %rdx
        add $8, %rdi
        dec %rsi

        cmp $0, %rdx
        jne loop
done:
        sub (%rsp), %rsi
        xor %rax, %rax
        sub %rsi, %rax
        pop %rsi
        pop %rdi
        leaveq
        retq

ちなみに MS の calling convention だと RDI を RCX にして RSI を RDX にしないといけないぽい、が、未確認。

http://msdn2.microsoft.com/en-us/library/ms794612.aspx

どうでもいいんだけどMSDN の StackWalk見てて、IMAGE_FILE_MACHINE_* ってこんな少ないんかい! と思って winnt.h とか見てたら結構たくさん出てきてびびった。

#define IMAGE_FILE_MACHINE_UNKNOWN  0x0000
#define IMAGE_FILE_MACHINE_AM33     0x01d3 /* Matsushita AM33 */
#define IMAGE_FILE_MACHINE_AMD64    0x8664 /* x64 */
#define IMAGE_FILE_MACHINE_ARM      0x01c0 /* ARM little endian */
#define IMAGE_FILE_MACHINE_EBC      0x0ebc /* EFI byte code */
#define IMAGE_FILE_MACHINE_I386     0x014c /* Intel 386 or later processors
                          and compatible processors */
#define IMAGE_FILE_MACHINE_IA64     0x0200 /* Intel Itanium processor family */
#define IMAGE_FILE_MACHINE_M32R     0x9041 /* Mitsubishi M32R little endian */
#define IMAGE_FILE_MACHINE_MIPS16   0x0266 /* MIPS16 */
#define IMAGE_FILE_MACHINE_MIPSFPU  0x0366 /* MIPS with FPU */
#define IMAGE_FILE_MACHINE_MIPSFPU16    0x0466 /* MIPS16 with FPU */
#define IMAGE_FILE_MACHINE_POWERPC  0x01f0 /* Power PC little endian */
#define IMAGE_FILE_MACHINE_POWERPCFP    0x01f1 /* Power PC with floating point s
upport */
#define IMAGE_FILE_MACHINE_R4000    0x0166 /* MIPS little endian */
#define IMAGE_FILE_MACHINE_SH3      0x01a2 /* Hitachi SH3 */
#define IMAGE_FILE_MACHINE_SH3DSP   0x01a3 /* Hitachi SH3 DSP */
#define IMAGE_FILE_MACHINE_SH4      0x01a6 /* Hitachi SH4 */
#define IMAGE_FILE_MACHINE_SH5      0x01a8 /* Hitachi SH5 */
#define IMAGE_FILE_MACHINE_THUMB    0x01c2 /* Thumb */
#define IMAGE_FILE_MACHINE_WCEMIPSV2    0x0169 /* MIPS little-endian WCE v2 */

CE とか Mobile とかで動いたりするんかな。

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h