3

更新: LinuxでGNUランタイムを使用しています。この問題は、Appleランタイムを使用するMacOSでは発生しません。

アップデート2: MacOSでGNUランタイムをコンパイルし、それを使用してサンプルをビルドしました。GNUランタイムのMacOSではエラーは発生しません。問題はglibcにあると思います(backtracebacktrace_symbolsはglibc拡張機能であるため)。

とを使用してGCCでコンパイルされたObjective-Cアプリでバックトレースを印刷するbacktracebacktrace_symbols、Objective-Cシンボルが表示されません。ファイル名、アドレス、およびC記号のみが表示されます。

でコンパイルし-gてリンクしまし-rdynamicた。

私のテストアプリ:

void _printTrace()
{
    void *addr[1024];
    int aCount = backtrace(addr, 1024);
    char **frameStrings = backtrace_symbols(addr, aCount);
    for (int i = 0; i < aCount; i++) {
        printf("%s\n", frameStrings[i]);
    }
    free(frameStrings);
}

@interface TheObject
+ (void)_printTrace;
+ (void)printTrace;
@end

@implementation TheObject
+ (void)_printTrace
{
    _printTrace();
}

+ (void)printTrace
{
    [self _printTrace];
}
@end

void printTrace()
{
    [TheObject printTrace];
}

int main(int argc, char **argv)
{
    printTrace();
    return 0;
}

そしてそれは出力です:

./test.bin(_printTrace+0x1f) [0x8048e05]
./test.bin() [0x8048e60]
./test.bin() [0x8048e8b]
./test.bin(printTrace+0x34) [0x8048ec5]
./test.bin(main+0xf) [0x8048eda]
/lib/libc.so.6(__libc_start_main+0xe5) [0xb7643bb5]
./test.bin() [0x8048b51]

このバックトレースにObjective-Cシンボルを表示させる方法はありますか?

4

3 に答える 3

3

dladdr()グローバルシンボルとウィークシンボルのみを報告します。ただし、Objective-C関数のシンボルはすべてローカルです。

$ readelf -s so_backtrace

Symbol table '.dynsym' contains 29 entries:
…

Symbol table '.symtab' contains 121 entries:
   Num:    Value  Size Type    Bind   Vis      Ndx Name
…
    49: 08048a01    13 FUNC    LOCAL  DEFAULT   14 _c_TheObject___printTrace
    50: 08048a0e    47 FUNC    LOCAL  DEFAULT   14 _c_TheObject__printTrace
…

GNU libcのソースコードを自分で調べることで、ローカルシンボルが返されないことを確認できます。backtrace_symbols()sysdeps / generic / elf/backtracesyms.cで定義されています。_dl_addr()elf / dl-addr.cで定義されているに依存して、シンボル名を提供します。それは最終的にを呼び出しますdetermine_info()。可能であれば、GNUハッシュテーブルを使用します。これには、設計上ローカルシンボルが含まれていません。

49       /* We look at all symbol table entries referenced by the hash
50          table.  */
…
60                   /* The hash table never references local symbols so
61                      we can omit that test here.  */

GNUハッシュテーブルが存在しない場合は、標準のハッシュテーブルにフォールバックします。これにはすべてのシンボルが含まれますが、determine_info()コードはグローバルシンボルと弱いシンボルを除くすべてを除外します。

90         if ((ELFW(ST_BIND) (symtab->st_info) == STB_GLOBAL
91              || ELFW(ST_BIND) (symtab->st_info) == STB_WEAK)

Objective-Cの関数アドレスをシンボリック化するには、ローカル関数のシンボルを除外するのではなく、自分でルックアップを実行する必要があります。さらに、に復元するには、Objective-C関数のシンボルをデマングルする必要があり_c_TheObject___printTraceます+[TheObject _printTrace]

于 2010-10-18T00:50:30.930 に答える
1

GNUstepのNSException実装はを使用せずbacktrace、代わりにlibbfd(バイナリファイル記述子)を使用します。実際に作業を行う関数はと呼ばれていると思いますが、こちらでstatic void find_addressご覧いただけます。この些細な例を使用して、次の結果が得られます。

#include <Foundation/Foundation.h>

@interface Test : NSObject {}
+ (void) test;
@end

@implementation Test
+ (void) test
{
    Class GSStackTrace = objc_getClass("GSStackTrace");

    id stack = [GSStackTrace currentStack];

    for (int i = 0; i < [stack frameCount]; i++)
    {
        NSLog (@"%@", [[stack frameAt:i] function]);
    }
}
@end

int main(int argc, char **argv)
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    [Test test];

    [pool release];

    return 0;
}

出力(デバッグシンボルを使用してコンパイルした場合):

2010-10-18 14:14:46.188 a.out[29091] +[GSStackTrace currentStack]
2010-10-18 14:14:46.190 a.out[29091] +[Test test]
2010-10-18 14:14:46.190 a.out[29091] main
2010-10-18 14:14:46.190 a.out[29091] __libc_start_main

あなたはバラバラにすることができるかもしれませんGSStackTrace。これは「プライベート」クラスです(そのためobjc_getClass、使用する必要があります。認識されないセレクター警告も多数表示されます)が、Objective-Cクラス名を読み取るために必要なすべてのコードが含まれているようです。

GNUstepがで構成されたUbuntu9.04でテストされています--enable-debugGSFunctionInfoビルドに含まれています)。

于 2010-10-18T03:16:37.967 に答える
-1

シンボル情報を取得するには、アドレスについてObjCランタイムに問い合わせる必要があると思います。たとえば、backtrace()から返されたアドレスをobject_getClass()のようなものに渡して、クラスを取得することができます。私はこれを試したことがありませんが、この場合は次に見ていきます。

于 2010-09-30T16:01:38.567 に答える